• Codeforces Round #596 Div1 A~E题解


    我好菜啊

    A

    题意:
    定义p-二进制数为2^k-p,给出n和p,求用最小个数的p-二进制数来表示n
    1<=n<=10^9,-1000<=p<=1000

    题解:
    猜结论,答案不会很大
    n可以表示成kp+s的形式,枚举k,判断(n-kp)是否能用k个2的幂构成
    画一下图可以发现,如果可以构成,那么满足(n-kp)的位数<=k<=n-kp
    (相当于把一颗二叉树上一个点变成两个)
    证明答案不会很大:
    首先(n-kp)的位数最多为30
    ①p>=0
    显然当k超过30后,如果不满足则之后也不满足
    ②p<0
    那么n-kp显然大于等于k

    code

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #define fo(a,b,c) for (a=b; a<=c; a++)
    #define fd(a,b,c) for (a=b; a>=c; a--)
    using namespace std;
    
    long long n,s;
    int p,i,j,k,l;
    
    bool pd(long long t)
    {
    	long long T=t;
    	int sum=0;
    	
    	while (T)
    	{
    		sum+=T&1;
    		T>>=1;
    	}
    	
    	if (s>=sum && s<=t)
    	return 1;
    	else
    	return 0;
    }
    
    int main()
    {
    //	freopen("a.in","r",stdin);
    	
    	scanf("%I64d%d",&n,&p);
    	
    	while (!pd(n))
    	{
    		n-=p;
    		++s;
    		
    		if (n<0 ||s>100)
    		break;
    	}
    	
    	if (n>=0 && pd(n))
    	printf("%I64d
    ",s);
    	else
    	printf("-1
    ");
    }
    

    B

    题意:
    给出n个数和k,求(i,j)的个数(i<j),使得ai*aj=x^k
    n<=10^5,2<=k<=100

    题解:
    能表示成x的k次方,意味着乘积质因数分解后每一位的指数%k=0
    也就是(ai中p的指数+aj中p的指数)%k=0,把i或j的指数取负,变成求ai和aj质因数指数相等的(i,j)个数
    可以排序搞,根据k讨论一下大质数

    code

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #define fo(a,b,c) for (a=b; a<=c; a++)
    #define fd(a,b,c) for (a=b; a>=c; a--)
    #define min(a,b) (a<b?a:b)
    using namespace std;
    
    int a[200001];
    int p[200001][66];
    int P[317];
    int c[200001];
    int N,n,K,i,j,k,l,s,len,ls,s1,s2;
    long long ans;
    bool bz;
    
    bool cmp(int a,int b)
    {
    	int i;
    	
    	fo(i,1,65)
    	if (p[a][i]<p[b][i])
    	return 1;
    	else
    	if (p[a][i]>p[b][i])
    	return 0;
    	
    	return 0;
    }
    
    bool pd(int t)
    {
    	int i;
    	
    	fo(i,1,65)
    	if (p[a[t]][i]!=p[a[t+1]][i])
    	return 0;
    	return 1;
    }
    
    bool Cmp(int x,int y)
    {
    	return p[x][0]<p[y][0];
    }
    
    int main()
    {
    //	freopen("b.in","r",stdin);
    	
    	fo(i,2,316)
    	{
    		k=1;
    		
    		fo(j,2,floor(sqrt(i)))
    		if (!(i%j))
    		{
    			k=0;
    			break;
    		}
    		
    		if (k)
    		P[++len]=i;
    	}
    	
    	scanf("%d%d",&n,&K);
    	fo(i,1,n)
    	{
    		scanf("%d",&l);
    		fo(j,1,len)
    		if (!(l%P[j]))
    		{
    			while (!(l%P[j]))
    			{
    				l/=P[j];
    				++p[i][j];
    			}
    			p[i][j]%=K;
    			p[i+n][j]=(K-p[i][j])%K;
    		}
    		
    		if (l>1)
    		{
    			p[i][0]=l;
    			p[i+n][0]=l;
    		}
    	}
    	
    	N=n+n;
    	fo(i,1,N)
    	a[i]=i;
    	
    //	fo(i,1,N)
    //	{
    //		fo(j,0,5)
    //		cout<<p[i][j]<<" ";
    //		cout<<endl;
    //	}
    	
    	sort(a+1,a+N+1,cmp);
    	
    //	fo(i,1,N)
    //	cout<<a[i]<<endl;
    //	fo(i,1,N)
    //	{
    //		fo(j,0,5)
    //		cout<<p[a[i]][j]<<" ";
    //		cout<<endl;
    //	}
    	
    	ls=1;
    	fo(i,1,N)
    	if (i==N || !pd(i))
    	{
    		l=0;
    		fo(j,ls,i)
    		c[++l]=a[j];
    		
    		sort(c+1,c+l+1,Cmp);
    		
    		if (K>=3)
    		{
    			s1=0;s2=0;
    			
    			fo(j,1,l)
    			if (!p[c[j]][0])
    			{
    				if (c[j]<=n)
    				++s1; else ++s2;
    			}
    			
    //			fo(j,1,l)
    //			if (c[j]<=n)
    //			cout<<c[j]<<" ";
    //			else
    //			cout<<-(c[j]-n)<<" ";
    //			cout<<endl;
    //			cout<<" "<<s1*s2<<" "<<s1<<" "<<s2<<endl;
    			
    			ans+=(long long)s1*s2;
    		}
    		else
    		{
    			s1=0;s2=0;
    			fo(j,1,l)
    			{
    				if (j==1 || p[c[j]][0]==p[c[j-1]][0])
    				{
    					if (c[j]<=n)
    					++s1; else ++s2;
    				}
    				else
    				{
    					ans+=(long long)s1*s2;
    					
    					s1=0;s2=0;
    					if (c[j]<=n)
    					++s1; else ++s2;
    				}
    			}
    			ans+=(long long)s1*s2;
    		}
    		
    		ls=i+1;
    	}
    	
    	if (K==2)
    	{
    		fo(i,1,n)
    		{
    			fo(j,1,65)
    			if ((p[i][j]*2%K))
    			break;
    			
    			if (j>65)
    			--ans;
    		}
    	}
    	else
    	{
    		fo(i,1,n)
    		if (!p[i][0])
    		{
    			fo(j,1,65)
    			if ((p[i][j]*2%K))
    			break;
    			
    			if (j>65)
    			--ans;
    		}
    	}
    	
    	printf("%I64d
    ",ans/2);
    }
    

    C

    题意:
    给出n*m的方格图,每个格子上有514石头或者为空
    从(1,1)开始向(n,m)移动(只能向右/下),每次移动会把一行/一列的石头向右/下推一格,不能把石头推出方格图外
    求方案数

    题解:
    差点就切了的sb题
    一个合法的路径有若干次转折,可以发现每次转折时所转到的方向上的石头都没被推过
    所以只需要考虑当前方向往后的石头个数即可转移,用前缀和优化

    code

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #define fo(a,b,c) for (a=b; a<=c; a++)
    #define fd(a,b,c) for (a=b; a>=c; a--)
    #define mod 1000000007
    using namespace std;
    
    bool a[2002][2002];
    int s1[2002][2002];
    int s2[2002][2002];
    int f[2002][2002][2];
    int g[2002][2002][2];
    int n,m,i,j,k,l;
    char ch;
    
    int main()
    {
    //	freopen("c.in","r",stdin);
    	
    	scanf("%d%d",&n,&m);
    	fo(i,1,n)
    	{
    		fo(j,1,m)
    		{
    			ch=getchar();
    			while (ch!='.' && ch!='R')
    			ch=getchar();
    			
    			a[i][j]=ch=='R';
    		}
    	}
    	
    	if (a[n][m])
    	{
    		printf("0
    ");
    		return 0;
    	}
    	if (n==1 && m==1)
    	{
    		printf("1
    ");
    		return 0;
    	}
    	
    	fd(i,n,1)
    	{
    		fd(j,m,1)
    		{
    			s1[i][j]=s1[i][j+1]+a[i][j];
    			s2[i][j]=s2[i+1][j]+a[i][j];
    		}
    	}
    	
    	f[1][1][0]=1;
    	f[1][1][1]=1;
    	fo(i,1,n)
    	{
    		fo(j,1,m)
    		{
    			if (i>1 || j>1)
    			{
    				f[i][j][0]=g[i][j][0];
    				f[i][j][1]=g[i][j][1];
    			}
    			
    			fo(k,0,1)
    			if (f[i][j][k])
    			{
    				if (!k)
    				{
    					g[i][j][k^1]=(g[i][j][k^1]+f[i][j][k])%mod;
    					g[i][m-s1[i][j+1]+1][k^1]=(g[i][m-s1[i][j+1]+1][k^1]-f[i][j][k])%mod;
    				}
    				else
    				{
    					g[i][j][k^1]=(g[i][j][k^1]+f[i][j][k])%mod;
    					g[n-s2[i+1][j]+1][j][k^1]=(g[n-s2[i+1][j]+1][j][k^1]-f[i][j][k])%mod;
    				}
    			}
    			
    			g[i][j+1][1]=(g[i][j+1][1]+g[i][j][1])%mod;
    			g[i+1][j][0]=(g[i+1][j][0]+g[i][j][0])%mod;
    		}
    	}
    	
    	printf("%d
    ",((f[n][m][0]+f[n][m][1])%mod+mod)%mod);
    }
    

    D

    题意:
    把一条链进行若干次操作,每次操作选择一个点,把这个点的父亲设为其父亲的父亲
    求最小的操作使得能把链变成给出的一棵树,并给出链的初始编号和每次操作的点编号

    题解:
    很妙的构造题
    一开始想把每个点接到兄弟节点中深度最小的点,但是挂了
    考虑把树变成链,一次操作实质是把一个点的父亲设为其的一个兄弟节点
    由于每次操作树的深度最多+1,所以操作次数的下限为(n-深度)
    先找出树上最长链,每次把最长链上的一个点v下移到兄弟u,下移后最长链多了u
    每次深度+1,所以这样的操作次数刚好是(n-深度),即为最优

    code

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #define fo(a,b,c) for (a=b; a<=c; a++)
    #define fd(a,b,c) for (a=b; a>=c; a--)
    #define min(a,b) (a<b?a:b)
    #define max(a,b) (a>b?a:b)
    using namespace std;
    
    int a[100001][2];
    int ls[100001];
    int fa[100001];
    int nx[100001];
    int Ans[100001];
    bool bz[100001];
    int d[100001];
    int n,i,j,k,l,len,ans,tot,tot2,mx,mx2,h,t;
    
    void New(int x,int y)
    {
    	++len;
    	a[len][0]=y;
    	a[len][1]=ls[x];
    	ls[x]=len;
    }
    
    void dfs(int t,int d)
    {
    	int i;
    	
    	if (d>mx)
    	mx=d,mx2=t;
    	
    	for (i=ls[t]; i; i=a[i][1])
    	fa[a[i][0]]=t,dfs(a[i][0],d+1);
    }
    
    int main()
    {
    //	freopen("d.in","r",stdin);
    	
    	scanf("%d",&n);
    	fo(i,2,n)
    	{
    		scanf("%d",&fa[i]);++fa[i];
    		New(fa[i],i);
    	}
    	
    	dfs(1,1);
    	
    	while (mx2)
    	{
    		bz[mx2]=1;
    		
    		nx[fa[mx2]]=mx2;
    		mx2=fa[mx2];
    	}
    	fo(j,1,n)
    	if (bz[j])
    	{
    		for (i=ls[j]; i; i=a[i][1])
    		if (!bz[a[i][0]])
    		d[++t]=a[i][0];
    	}
    	
    	while (h<t)
    	{
    		++h;
    		j=nx[fa[d[h]]];
    		
    		for (i=ls[d[h]]; i; i=a[i][1])
    		d[++t]=a[i][0];
    		
    		fa[j]=d[h];
    		nx[fa[d[h]]]=d[h];
    		nx[d[h]]=j;
    		
    		Ans[++tot]=j;
    	}
    	
    	for (i=1; i; i=nx[i])
    	printf("%d ",i-1);
    	printf("
    ");
    	printf("%d
    ",tot);
    	fd(i,tot,1)
    	printf("%d ",Ans[i]-1);
    }
    

    E

    题意:
    给出一个数k和n个数(都不能整除k),每次可以把两个数合并,合并后的值除k直到不整除为止
    求一种把n个数合并成1的方案
    n<=16,k<=2000,∑ai<=2000

    题解:
    O(3^n*2000)的做法显然过不了
    可以发现,每种合并的方案最终都可以表示为∑ai*k^bi,其中bi<0
    证明每种∑ai*k^bi=1的情况都能对应一种合法方案:
    ①n=1
    那么只有a1=1时才成立
    (不存在a1>1的情况)
    ②n>1
    设序列中最小的bi为B,那么必定存在至少两个bi=B
    证明:
    若只有一个,那么
    ∑ai*k^bi=1
    ∑ai*k^(bi-B)=k^(-B)
    当bi>B时,乘积必为k的倍数,而右侧也为k的倍数
    如果只有一个bi=B,且ai%k≠0,左右在模k意义下不等,实际也必然不等

    那么每次把bi=bj=B的ij合并,把新的数的b变为(b+合并后的数除k的次数),合并的数变为f(i+j)即可
    可以发现这样仍满足∑ai*k^bi=1,而n-1的所有情况都已经归纳证明了
    ③n=2
    由于n=1比较特殊,所以n=2也要特殊考虑
    从n>1的结论得知,b1=b2=B
    因为a1*k^B+a2*k^B=1
    a1+a2=k^(-B)
    所以f(a1+a2)=1,可以变为n=1的情况,即上文所说不存在a1>1


    那么dp就很显然了,考虑每次操作加一个a,或者对于所有的a除以k
    把一种合法的序列还原时,可以发现其中不存在小数
    所以dp时的ai和也必为整数
    用bitset优化,反着还原dp,然后正着求出每次操作
    还原dp时不需要记录上个状态(因为有bitset),每次枚举一种操作判断原状态是否存在即可

    code

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <bitset>
    #define fo(a,b,c) for (a=b; a<=c; a++)
    #define fd(a,b,c) for (a=b; a>=c; a--)
    using namespace std;
    
    struct type{
    	int a,b;
    } b[17];
    int p[17];
    int a[17];
    int ans[101];
    bitset<2001> f[65536];
    int n,K,L,i,j,k,l,sum,len,s,tot;
    
    bool cmp(type a,type b)
    {
    	return a.b>b.b;
    }
    
    int main()
    {
    //	freopen("e.in","r",stdin);
    	
    	p[1]=1;
    	fo(i,2,16)
    	p[i]=p[i-1]<<1;
    	
    	scanf("%d%d",&n,&K);L=(p[n]<<1)-1;
    	fo(i,1,n)
    	scanf("%d",&a[i]),sum+=a[i];
    	sum/=K;
    	
    	f[0][0]=1;
    	fo(i,1,L)
    	{
    		fo(j,1,n)
    		if (i&p[j])
    		f[i]|=f[i^p[j]]<<a[j];
    		
    		fd(j,sum,1)
    		if (f[i][j*K])
    		f[i][j]=1;
    	}
    	
    	if (!f[L][1])
    	printf("NO
    ");
    	else
    	{
    		printf("YES
    ");
    		
    		s=L;j=1;
    		while (j)
    		{
    			if (j<=sum && f[s][j*K])
    			{
    				j*=K;
    				ans[++len]=-1;
    				continue;
    			}
    			
    			fo(i,1,n)
    			if (s&p[i] && j>=a[i] && f[s-p[i]][j-a[i]])
    			{
    				s-=p[i];
    				j-=a[i];
    				ans[++len]=i;
    				
    				break;
    			}
    		}
    		
    		j=0;
    		fo(i,1,len)
    		if (ans[i]==-1)
    		++j;
    		else
    		b[++tot]={a[ans[i]],-j};
    		
    		while (tot>1)
    		{
    			sort(b+1,b+tot+1,cmp);
    			
    			printf("%d %d
    ",b[tot-1].a,b[tot].a);
    			b[tot-1].a+=b[tot].a;
    			
    			--tot;
    			while (!(b[tot].a%K))
    			b[tot].a/=K,++b[tot].b;
    		}
    	}
    }
    
  • 相关阅读:
    后台性能测试不可不知的二三事
    linux下操作mysql
    loadrunner scripts
    反射
    java 读取json
    java 多线程
    python_day11
    python爬虫
    python_day10
    python_day9
  • 原文地址:https://www.cnblogs.com/gmh77/p/11779812.html
Copyright © 2020-2023  润新知