2020中國大學生程式設計競賽(CCPC) - 網路選拔賽

2020-09-24 17:00:57

01
Art Class

02
Graph Theory Class
考慮一個一個點加,那麼新加的點x要是是合數,那麼與某個因子連上,整體權值就是增加x;要是x是質數,那麼與2相連,整體全值增加兩倍x。這樣是最優的連線情況。
Min25篩求n+1以內質數字首和,再加上 2+3+4+…n+1 即可。

#include <cstdio>
#include <cstring>
using namespace std;
const long long Nmax=10000000005ll;
const long long SQR=100005ll;
long long N,Ha,Haa,g[SQR*3],ans;
long long w[SQR*3],id1[SQR],id2[SQR],cnt;
long long f[SQR],P[SQR],SP[SQR],num;

void get_pri(long long Pmax)
{
	num=0;
	memset(f,0,sizeof(f));
	for (long long i=2; i<=Pmax; i++)
	{
		if (!f[i])
		{
			P[++num]=i;
			SP[num]=SP[num-1]+i;
		}
		for (long long j=1,k; j<=num && (k=i*P[j])<=Pmax; j++)
		{
			
			f[k]=1;
			if (i%P[j]==0)
				break;
		}
	}
}

void get_id()
{
	cnt=0;
	for (long long i=1,k; i<=N; )
	{
		k=N/i;			//g[k] 是需要的,將 k 離散出來 
		w[++cnt]=k;		//離散出的數中第 cnt 個為 k  
		if (k<=SQR)
			id1[k]=cnt;
		else
			id2[N/k]=cnt;
		i=N/k+1;	//使 k 跳到往後使 N/k 加 1 
	}
}

inline int ID(long long x){return (x<=SQR ? id1[x] : id2[N/x]);}

void get_g()
{
	long long X;
	for (int i=1; i<=cnt; i++)		//dp初始化, 即 j=0 時 g[w][j] 等於2~w累加 
	{
		g[i]=(w[i]+2)*(w[i]-1)/2;
	}
	for (int j=1; j<=num; j++)
	{
		for (int i=1; i<=cnt; i++)
		{
			if (P[j]*P[j]>w[i]) break;
			//X=((g[ID(w[i]/P[j])]-SP[j-1])%Ha+Ha)%Ha;
			X=g[ID(w[i]/P[j])]-SP[j-1];
			g[i]=g[i]-P[j]*X;
			//if (g[i]<0) g[i]=(g[i]%Ha+Ha)%Ha;
			//if (g[i]>Ha) g[i]%=Ha;
		}
	}
}

int main()
{
	long long T,n;
	scanf("%lld",&T);
	get_pri(SQR);

	
	while(T--)
	{
		ans=0;
		scanf("%lld%lld",&n,&Ha);
		Haa=Ha*Ha;
		N=n+1;
		get_id();
		get_g();
		ans=g[ID(N)]%Ha;
		ans=(ans+(n+3)%Ha*n%Ha*(Ha+1)/2%Ha)%Ha;		//2+3+...+(n+1)
		ans=(ans-4+Ha)%Ha;
		printf("%lld\n",ans);
	}
	
	return 0;
}

/*
1
260136231 19260817


*/

03
Chess Class

04
Chess Class

05
Lunch

06
CCPC Training Class
舉幾個例子就發現把出現最多的字母全部連著擺最開頭就是最優情況了。

#include <cstdio>
int T,a[30],ans;
char ch;

int main()
{
    scanf("%d",&T);
    ch='a'-5;
    for (int kk=1; kk<=T; kk++)
    {
        for (int i=0; i<26; i++)
        	a[i]=0;
        while (ch<'a' || ch>'z')
        	ch=getchar();
        while (ch>='a' && ch<='z')
        {
            a[ch-'a']++;
            ch=getchar();
        }
        ans=0;
        for (int i=0; i<26; i++)
        	if (ans<a[i])
        		ans=a[i];
        printf("Case #%d: %d\n",kk,ans);
    }
    return 0;
}

07
PE Class

08
Math Class

09
Reports

10
3x3 Convolution
這個矩陣乘法形象點就是將K左上角扣在A上點(i,j)上,然後將重合的位置相乘之和賦給新的點(i,j)上,要是K上某些點位置超出A了,就不理K那些位置(就是說能乘就乘。)

在這裡插入圖片描述

看樣例,可以猜一下答案不是原樣輸出就是全是0。
想想什麼時候是原樣輸出:只有當"K僅有左上角那個元素非零"時答案時原樣輸出。
再想想K是其它情況的時候是不是答案是0:以A右下角的點為突破口,由於K元素和為1,若K除左上角外其它地方還有非零元素,那麼對於A右下角的點,每次乘法過後它都會減少,而且成等比減少,趨於0,這樣一來,A右下角周圍的點類似也會跟著一直減小,以此類推,整個A最終都會趨於0.
於是問題解決了。注意一下格式(行末空格)即可。

#include <cstdio>

int main()
{
    //freopen("1.txt","w",stdout);
    int T,n,f,a[1000][1000],b[10];
    scanf("%d",&T);
    while (T--)
    {
        f=0;
        scanf("%d",&n);
        for (int i=1; i<=n; i++)
            for (int j=1; j<=n; j++)
                scanf("%d",&a[i][j]);
        for (int i=1; i<=9; i++)
        {
            scanf("%d",&b[i]);
            f+=b[i];
        }
        if (b[1]==f && f>0)
        {
            for (int i=1; i<=n; i++)
            {
                for (int j=1; j<=n; j++)
                {
                    if (j==n)
                        printf("%d",a[i][j]);
                    else
                        printf("%d ",a[i][j]);
                }
                printf("\n");
            }
        }
        else
        {
            for (int i=1; i<=n; i++)
            {
                for (int j=1; j<=n; j++)
                {
                    if (j==n)
                        printf("0");
                    else
                        printf("0 ");
                }
                printf("\n");
            }
        }
        
    }
    return 0;
}

11
Xor

12
Residual Polynomial