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)}2\le 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)\)暴力處理一下就好了。

接着直接開始找。字典序最小怎麼辦?一開始我這個菜雞腦袋短路了,問了下神仙\(\text{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\) ),構造總次數不超過\(\lfloor\frac n 3\rfloor+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\n%d\n",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\n",++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\n",j-2*nd,j-nd,j);
		}
		if(id2[x]){
			R j=i+c[id2[x]],nd=d[id2[x]];
			printf("%d %d %d\n",j-2*nd,j-nd,j);
		}
	}
	return 0;
}

F

給兩個整數\(a,b\),每次操作可以對一個數乘上一個質數、或除以它的一個質因數,求使兩數約數個數相等的最少操作次數。

顯然跟唯一分解有關,把數分解成\(\prod p_i^{k_i}\)以後,所有的\(k_i\)就可以壓進狀態。然後就搜?DP?最短路?

更麻煩的是狀態的上界不好控制。然後這題就咕咕了吧。。。

Div1 E

當計算幾何練手題做了,題解戳這兒

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章