• Codeforces Round #517 Div. 2/Div. 1


    (n)天没更博了,因为被膜你赛的毒瘤题虐哭了。。。

    既然打了这次CF还是纪念一下。

    看看NOIP之前,接下来几场的时间都不好。这应该是最后一场CF了,差(4)分上紫也是一个遗憾吧。

    A

    给一个矩形,每次从外面剥掉一圈,按剥去次序的奇偶分开,问最前(k)个奇数圈的总面积。

    普及组题,直接模拟。

    #include<bits/stdc++.h>
    #define LL long long
    #define RG register
    #define R RG int
    using namespace std;
    int main(){
    	LL w,h,k,ans=0;
    	cin>>w>>h>>k;
    	while(k--){
    		ans+=2*(w+h-2);
    		w-=4;h-=4;
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

    B

    给两个长度为(n)的序列({a},{b}),构造一个长度为(n)的序列({t})使得(t_i|t_{i+1}=a_i,t_i&t_{i+1}=b_i)

    一看到题就想着乱搞,找到一个合法的就直接构造。

    不想DFS了,因为感觉如果直接构造WA了的话DFS不是也会TLE么?

    然后就过了。。。

    #include<bits/stdc++.h>
    #define LL long long
    #define RG register
    #define R RG int
    using namespace std;
    const int N=100009;
    int n,a[N],b[N],t[N];
    bool check(){
    	for(R i=0;i<n-1;++i){
    		for(t[i+1]=0;t[i+1]<4;++t[i+1])
    			if((t[i]|t[i+1])==a[i]&&(t[i]&t[i+1])==b[i])break;
    		if(t[i+1]==4)return 0;
    	}
    	return 1;
    }
    int main(){
    	ios::sync_with_stdio(0);
    	cin>>n;
    	for(R i=0;i<n-1;++i)cin>>a[i];
    	for(R i=0;i<n-1;++i)cin>>b[i];
    	for(t[0]=0;t[0]<4;++t[0])
    		if(check()){
    			puts("YES");
    			for(R i=0;i<n;++i)printf("%d ",t[i]);
    			return puts(""),0;
    		}
    	puts("NO");
    	return 0;
    }
    

    C

    给定正整数(a,b),找到最大的正整数(s),要求所有(1-s)的数能被分成两组,第一组的和(le a),第二组的和(le b)

    又是乱搞。。。

    首先(s)有个上限,(frac{s(s+1)}2le a+b)。然后(s)取到上限的时候一定可以构造?

    然后把数从大往小丢进(a)里,能丢就丢。剩下的就给(b)了。

    然后因为(s)的极限值估计错了数组开小RE一发。然后就过了。如果这里少罚点时应该也能上紫了。

    #include<bits/stdc++.h>
    #define LL long long
    #define RG register
    #define R RG int
    using namespace std;
    bool f[70000];
    int main(){
    	RG LL a,b,s,p=0;
    	cin>>a>>b;
    	s=(sqrt(8*(a+b)+1)-1)/2.0;
    	for(R i=s;i;--i)
    		if(a>=i)a-=i,f[i]=1,++p;
    	cout<<p<<endl;
    	for(R i=1;i<=s;++i)if( f[i])printf("%d ",i);puts("");
    	cout<<s-p<<endl;
    	for(R i=1;i<=s;++i)if(!f[i])printf("%d ",i);puts("");
    	return 0;
    }
    

    D

    给一个(n*n)的字母方阵,可以把(k)个字母改成a,求字典序最小的从((1,1))((n,n))的路径。

    首先,把路径最前面不是a的换成a显然更优。那么如果((1,1))到某个位置的路径上最多有不超过(k)个字母不为a,这个位置可以而且必须改成a。(O(n^2))暴力处理一下就好了。

    接着直接开始找。字典序最小怎么办?一开始我这个菜鸡脑袋短路了,问了下神仙( ext{yyb}),“不就是按副对角线转移么?”,瞬间懂了。。。

    找字典序最小肯定要在相同长度的情况下贪心。而以同一条副对角线结尾的路径长度当然相同啦!每次字典序最小的都是一个集合,直接用这个集合的所有后继找到下一长度的字典序最小的集合。

    调了半天,只是因为最小后继初始化的时候放错了位置。。。后面就没时间杠E了。如果这里少罚点时应该也能上紫了。

    #include<bits/stdc++.h>
    #define LL long long
    #define RG register
    #define R RG int
    using namespace std;
    const int N=2009;
    char s[N][N],ans[N];
    int f[N][N],X[2][N],Y[2][N];
    bool pr[N][N],vis[N][N];
    inline void chkmn(R&x,R y){if(x>y)x=y;}
    int main(){
    	R n,k;
    	RG char mn;
    	cin>>n>>k;
    	for(R i=1;i<=n;++i)cin>>(s[i]+1);
    	for(R i=1;i<=n;++i)
    		for(R j=1;j<=n;++j){
    			f[i][j]=(i==1&&j==1?0:3*N);
    			if(i!=1)chkmn(f[i][j],f[i-1][j]);
    			if(j!=1)chkmn(f[i][j],f[i][j-1]);
    			f[i][j]+=s[i][j]!='a';
    			if(f[i][j]<=k)s[i][j]='a';
    		}
    	R*x1=X[0],*x2=X[1],*y1=Y[0],*y2=Y[1],p1=1,p2=0,x,y;
    	x1[1]=y1[1]=1;
    	for(R i=1;i<=2*n;++i){
    		mn=127;
    		for(;p1;--p1){
    			x=x1[p1];y=y1[p1];
    			if(x<n&&!vis[x+1][y]&&mn>=s[x+1][y]){
    				if(mn>s[x+1][y])mn=s[x+1][y],p2=1;
    				else ++p2;
    				vis[x2[p2]=x+1][y2[p2]=y]=1;
    			}
    			if(y<n&&!vis[x][y+1]&&mn>=s[x][y+1]){
    				if(mn>s[x][y+1])mn=s[x][y+1],p2=1;
    				else ++p2;
    				vis[x2[p2]=x][y2[p2]=y+1]=pr[x][y+1]=1;
    			}
    		}
    		swap(x1,x2);swap(y1,y2);swap(p1,p2);
    	}
    	for(x=n,y=n;x!=1||y!=1;pr[x][y]?--y:--x)
    		ans[x+y-2]=s[x][y];
    	ans[0]=s[1][1];
    	puts(ans);
    	return 0;
    }
    

    E

    给一个(01)序列,每次可以翻转(x,y,z)三个位置的数位(假设(x<y<z),那么需满足(y-x=z-y) ),构造总次数不超过(lfloorfrac n 3 floor+12)的操作序列使原序列全变成(0)

    赛场上以为要想fstqwq的星空那题一样异或差分还差分什么的。然后就结束了。

    晚上发现了一个有用的思路:从后往前做,第(i)位碰到(1)就做一次((i-2,i-1,i))的操作,最后只会剩下前(3)位,这时候如果还不能直接消掉,就是剩下一个或者两个(1),而剩两个(1)又可以直接转化成剩一个(1)

    手动模拟,假如只有第一位一个(1),可以通过((1,4,7)(3,5,7)(3,4,5))把它消掉。这个(1)在第二位、第三位也是一样。那也就是说,如果序列长度大于等于(9),则一定有解:小于(9)有没有解?直接状压暴搜都够了。

    现在问题在于怎样减少操作次数。既然是(frac n 3),我们自然会想,可不可以只用一次操作就使最后(3)位都归零呢?有一个特例(011),尝试失败了。接着,我们只好尝试可不可以只用两次操作使最后(6)位归零。经过手动模拟(状态(2^6),慎用)、打表暴枚之后,我们可以证明尝试成功了。

    然后就用打出来的表一个个归零啊,等到序列长度小于(12)的时候,直接开状压BFS暴搜。

    #include<bits/stdc++.h>
    #define LL long long
    #define RG register
    #define R RG int
    using namespace std;
    const int N=1e5+9,M=99,Q=2099;
    int a[N],ax[N],c[M],d[M],id1[M],id2[M],q[Q],dis[Q],pr[Q];
    int main(){
    	R n,p=0,ans=0,x,y;
    	cin>>n;
    	for(R i=1;i<=n;++i)cin>>a[i];
    	for(R i=0;i<6;++i)
    		for(R j=1;i-2*j>=-6;++j){
    			c[++p]=i;d[p]=j;
    			x=1<<i;x|=(x>>j)|(x>>2*j);
    			if(!id1[x])id1[x]=p;
    		}
    	for(R i=1;i<=p;++i)
    		for(R j=1;j<=p;++j){
    			x=1<<c[i];x|=(x>>d[i])|(x>>2*d[i]);
    			y=1<<c[j];y|=(y>>d[j])|(y>>2*d[j]);
    			if(!id1[x^=y])id1[x]=i,id2[x]=j;
    		}
    	id1[0]=id2[0]=0;
    	for(R i=n-5;i>6;i-=6){
    		x=0;for(R j=i+5;j>=i;--j)(x<<=1)|=a[j];
    		ax[i]=x;
    		if(id1[x]){
    			R j=i+c[id1[x]],nd=d[id1[x]];
    			a[j-2*nd]^=1;a[j-nd]^=1;a[j]^=1;++ans;
    		}
    		if(id2[x]){
    			R j=i+c[id2[x]],nd=d[id2[x]];
    			a[j-2*nd]^=1;a[j-nd]^=1;a[j]^=1;++ans;
    		}
    	}
    	R S=1<<(p=min(n,11));
    	memset(dis,-1,4*S);
    	x=0;for(R j=p;j;--j)(x<<=1)|=a[j];
    	dis[q[0]=x]=0;
    	for(R h=0,t=0;h<=t;++h){
    		R x=q[h];if(!x)break;
    		for(R i=2;i<p;++i)
    			for(R j=i>>1;j;--j){
    				y=x^(1<<(i-2*j))^(1<<(i-j))^(1<<i);
    				if(!~dis[y])dis[q[++t]=y]=dis[pr[y]=x]+1;
    			}
    	}
    	if(!~dis[0])return puts("NO"),0;
    	printf("YES
    %d
    ",ans+dis[0]);
    	for(x=0;pr[x];x=pr[x]){
    		R t=0;
    		for(y=x^pr[x];~y&1;y>>=1,++t);printf("%d ",++t);
    		for(y>>=1    ;~y&1;y>>=1,++t);printf("%d ",++t);
    		for(y>>=1    ;~y&1;y>>=1,++t);printf("%d
    ",++t);
    	}
    	for(R i=n-5;i>6;i-=6){
    		x=ax[i];
    		if(id1[x]){
    			R j=i+c[id1[x]],nd=d[id1[x]];
    			printf("%d %d %d
    ",j-2*nd,j-nd,j);
    		}
    		if(id2[x]){
    			R j=i+c[id2[x]],nd=d[id2[x]];
    			printf("%d %d %d
    ",j-2*nd,j-nd,j);
    		}
    	}
    	return 0;
    }
    

    F

    给两个整数(a,b),每次操作可以对一个数乘上一个质数、或除以它的一个质因数,求使两数约数个数相等的最少操作次数。

    显然跟唯一分解有关,把数分解成(prod p_i^{k_i})以后,所有的(k_i)就可以压进状态。然后就搜?DP?最短路?

    更麻烦的是状态的上界不好控制。然后这题就咕咕了吧。。。

    Div1 E

    当计算几何练手题做了,题解戳这儿

  • 相关阅读:
    树莓派4B
    SpringBoot 自定义 info Actuator 原理
    RestTemplate翻译serviceId过程
    ISA, ABI, API区别
    01.编译器结构
    【Git123】Git SSH Key支持多账号
    【消息中间件123】Solace PubSub+ Event Broker介绍
    【ETL123】
    【Http123】Http Timeout
    【性能123】Linux性能之“平均负载”
  • 原文地址:https://www.cnblogs.com/flashhu/p/9835860.html
Copyright © 2020-2023  润新知