阶段codeforces小结

这些天除了每天的训练刷了一些的CD,最近状态奇差,那就总结一下吧

Codeforces Round #236 (Div. 2)

C:这题猜了一下,大致就是让边均匀分布,构造了一下感悟了一下,挺对

D:看数据范围和时间大致是n*n的复杂度,以为是任意选取区间,后来发现必须是1-k(1<=k<=n),可以贪心,后面的不会对前面有影响(*),然后倒着贪心,暴力分解,并且去掉了define就过了

Codeforces Round #237 (Div. 2)

C:根据某个点到各个点的距离恢复原图,并且保证每个店度数不超过k。合法的图只有一个距离为0,那么按照距离从小到大,依次把每个点连最多k-1(这个点已经连了父亲),不合法就跳出

D:这题和2016年ccpc合肥的E很像,但我不会。学习了网上的代码,dp[i][0-3]代表了第i个点和第i+1个点的四种状态(*)

Codeforces Round #238 (Div. 2)

C:这题没能第一时间想出来。把行列式推导结果写出来发现除了对角线,其他的未知的元素全部作了2次的贡献,那么这个01行列式的值只与对角线有关,并且每次翻转,无论如何都会改变行列式的值

D:对每一个X挑出相应的Y,如果X中有这个Y,那么代表着X的集合中有两个元素为1e6,那Y随便挑一组就可以。为什么一定能挑出来?因为数据范围:和为1e6,只有5e5个数,CF的数据范围好有灵性

Codeforces Round #239 (Div. 2)

C:给定a,b求构造一个三角形边都不在直角坐标系中(并且整点)。都不在就说明这个边是一个勾股数,并且这个勾股数还必须相等,因为要构造的三角形是直角三角形,枚举一下再判合法

D:这题发现了出来每个房间的时候次数一定为偶数,也就是说穿越回来和刚进来这个房间没有区别,那么可以记忆化搜索一下

LL dfs(int l,int r){
	if(dp[l][r]!=-1)return dp[l][r];
	if(l==r)return dp[l][r]=1;
	dp[l][r]=0;
	dp[l][r]=(dfs(l,r-1)+dfs(A[r-1],r-1)+1)%mod;
	return dp[l][r];
}
Codeforces Round #240 (Div. 2)

C:构造n个数使得(1,2),(3,4)....(2k+1,2k+2)的GCD恰好为K,k大于n/2一定不可以(两两gcd为1),那么第一个满足k-n/2+1,剩下的贡献保证1(从第一组的最后一个数开始的连续的数)

D:dp[i][j]长度为i最后一个数为j的序列个数,预处理一下因子

Codeforces Round #241 (Div. 2)

C:贪心放,把收益最高的人群放入桌子大于人数最小的一个

D:给一串序列,-1代表这个数未知,使得等差序列的个数最小((8, 6, 4, 2), (1, 4, 7, 10) and (2)),每次找到两个已经给出的数,依据合法性处理,然后再向后延伸,注意(3 2 -1 -1 -1 -1 -1 2 是2个等差序列 3 2 2 2 2 2 2 2 2)

Codeforces Round #242(Div. 2)

C:

把拆开来(1-n)mod 1^(1-n)mod 2^……mod n然后发现每一项有规律

if((n/i)&1){
	ans^=A[n%i];
	ans^=A[i-1];
}else {
	ans^=A[n%i];
}

D:n^4的枚举起点终点 擦线过了题

Codeforces Round #243 (Div. 2)

C:枚举区间 从大到小换区间里面的数(第一次20分钟切的题,刚刚竟然忘了怎么做,真的要早睡觉)

D:合法情况只有相邻两行完全一样或者完全不一样(*) 

if(m<=k){
		int all=1<<m;all--;
		int minm=1e9;
		for(int sta=0;sta<=all;sta++){
			int tmp=0;
			for(int i=1;i<=n;i++){
				int cnt=0;
				for(int j=1;j<=m;j++)
					if((1<<(j-1)&sta)>0&&mp[i][j]==1)cnt++;
					else if((1<<(j-1)&sta)==0&&mp[i][j]==0)cnt++;
				cnt=min(cnt,m-cnt);
				tmp+=cnt;
			}
			minm=min(minm,tmp);
		}
		//cout<<minm<<endl;
		if(minm==1e9)puts("0");
		else if(minm>k)printf("-1\n");
		else printf("%d\n",minm);
	}
	else {
		int minm=1e9;
		for(int sel=1;sel<=m;sel++){
			int tmp=0;
			for(int j=1;j<=m;j++){
				if(j==sel)continue;
				int cnt=0;
				for(int i=1;i<=n;i++)
					if(mp[i][j]==mp[i][sel])cnt++;
				cnt=min(cnt,n-cnt);
				tmp+=cnt;
			}
			//cout<<tmp<<endl;
			minm=min(minm,tmp);
		}
		if(minm==1e9)puts("0");
		else if(minm>k)printf("-1\n");
		else printf("%d\n",minm);
	}

Codeforces Round #244 (Div. 2)

C:缩点并且统计这个联通块中最小值的个数,个数乘起来得到方法种数,然后那个最小值不要取模

D:每个串对自己求最长连续子串,+1之后代表在i为右端点的时候最小多长不会在原串中重复,A,B都求一遍。然后AB求最长连续子串,合法的要求是

for(int i=1;i<=lenA;i++)
	for(int j=1;j<=lenB;j++){
		if(A[i]==B[j])dp[i][j]=dp[i-1][j-1]+1;
		else dp[i][j]=0;
		if(dp[i][j]>=maxA[i]&&dp[i][j]>=maxB[j])
			ans=min(ans,max(maxA[i],maxB[j]));
	}

Codeforces Round #245 (Div. 2)

C:dfs往下记录偶数距离和奇数距离的flag

void dfs(int u,int pre,int flg1,int flg2,int dist)

D:学校dp专题有原题。预处理四个顶点出来的最大值

for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
		mp[0][i][j]=max(mp[0][i-1][j],mp[0][i][j-1])+A[i][j];
for(int i=n;i>=1;i--)
	for(int j=m;j>=1;j--)
		mp[1][i][j]=max(mp[1][i+1][j],mp[1][i][j+1])+A[i][j];
for(int i=1;i<=n;i++)
	for(int j=m;j>=1;j--)
		mp[2][i][j]=max(mp[2][i-1][j],mp[2][i][j+1])+A[i][j];
for(int i=n;i>=1;i--)
	for(int j=1;j<=m;j++)
		mp[3][i][j]=max(mp[3][i+1][j],mp[3][i][j-1])+A[i][j];

然后上下 左右 或者左右 上下(*)

ans=max(ans,mp[0][i-1][j]+mp[1][i+1][j]+mp[2][i][j+1]+mp[3][i][j-1]);
ans=max(ans,mp[0][i][j-1]+mp[1][i][j+1]+mp[2][i-1][j]+mp[3][i+1][j]);

Codeforces Round #246 (Div. 2)

C:dp[i][j]距离为i最大为j的情况,每个节点出来都是1-k(*)

D:找一个n使得n-2*n之间的数恰好有m个数有k个0。用m作为约束条件,因为这个n具有单调性,二分这个n,用只有01的数位dp统计

LL dfs(int pos,int sum,int mask){
	if(pos==0)return 1LL*sum==k;
	if(!mask&&dp[pos][sum]!=-1)return dp[pos][sum];
	LL ans=0;
	int LIM=mask?bits[pos]:1;
	for(int i=0;i<=LIM;i++)ans+=dfs(pos-1,sum+(i==1),mask&&i==bits[pos]);
	if(!mask)dp[pos][sum]=ans;
	return ans;
}

Codeforces Round #247 (Div. 2)

C:素数从大往小放,因为n以内的素数最多log(n)个,然后放到最后要么差2个要么差3个这些(4是2+2,5是2+3以此类推)贪心放下去就可以。这个5n应该最坏情况下一个数要5次(猜的)

D:之前训练过next,看到这个就想到了next。

for(int i=0;s[i];i++)ans[NEXT[i]]++;
int k=NEXT[len-1];
//cout<<NEXT[1]<<endl; 
v.push_back({len,1});
for(int i=len-1;i>=0;i--)
	if(NEXT[i-1]>=0)
		ans[NEXT[i-1]]+=ans[i];

Codeforces Round #248 (Div. 2)

C:最多换一次,那就枚举这一次。统计每个数相邻的差值之和*(要去掉和他自己一样的),排序之后贪心换

D:看了卿学姐的做法,每个点维护上下左右到的最大值,

void update(int x,int y){
	mp[x][y]^=1;
	for(int i=1;i<=n;i++)
		if(mp[i][y])U[i][y]=U[i-1][y]+1;
		else U[i][y]=0; 
	for(int i=n;i>=1;i--)
		if(mp[i][y])D[i][y]=D[i+1][y]+1;
		else D[i][y]=0;
	for(int i=1;i<=m;i++)
		if(mp[x][i])L[x][i]=L[x][i-1]+1;
		else L[x][i]=0;
	for(int i=m;i>=1;i--)
		if(mp[x][i])R[x][i]=R[x][i+1]+1;
		else R[x][i]=0;
} 
int query(int x,int y){
	int maxU=U[x][y],maxD=D[x][y],ans=0; 
	if(mp[x][y])ans=max(ans,maxD+maxU-1);
	//cout<<maxD<<" "<<maxU<<endl;
	for(int j=y+1;j<=m;j++){
		maxU=min(maxU,U[x][j]);
		maxD=min(maxD,D[x][j]);
		ans=max(ans,(j-y+1)*(maxD+maxU-1));
	}
	maxU=U[x][y],maxD=D[x][y];
	for(int j=y-1;j>=1;j--){
		maxU=min(maxU,U[x][j]);
		maxD=min(maxD,D[x][j]);
		ans=max(ans,(y-j+1)*(maxD+maxU-1));
	}
	int maxL=L[x][y],maxR=R[x][y];
	if(mp[x][y])ans=max(ans,maxL+maxR-1);
	for(int i=x-1;i>=1;i--){
		maxL=min(maxL,L[i][y]);
		maxR=min(maxR,R[i][y]);
		ans=max(ans,(x-i+1)*(maxL+maxR-1));
	}
	maxL=L[x][y];maxR=R[x][y];
	for(int i=x+1;i<=n;i++){
		maxL=min(maxL,L[i][y]);
		maxR=min(maxR,R[i][y]);
		ans=max(ans,(i-x+1)*(maxL+maxR-1)); 
	}
	return ans;
}

Codeforces Round #249 (Div. 2)

C:因为y会小于0,用1000作为y的基准

D:枚举直角顶点,8种情况,再处理行列的前缀和 和斜过来和反斜过来的前缀和用来快速判断是否有1(写的很长)

for(int i=1;i<=n;i++){
	for(int j=1;j<=m;j++){
		if(mp[i][j]=='1')continue;
		for(int k=1;k+i<=n&&k+j<=m;k++){//shunshizhen
			if(mp[i][j+k]=='1'||mp[i+k][j]=='1')break;
			if(syum[i+k][j]-syum[i-1][j+k+1]>=1)continue;
			dp[i][j]++;
		}
		for(int k=1;i+k<=n&&j+k<=m&&j-k>=1;k++){
			if(mp[i+k][j-k]=='1'||mp[i+k][j+k]=='1')break;
			if(sumy[i+k][j+k]-sumy[i+k][j-k-1]>=1)continue;
			dp[i][j]++;
		}
		for(int k=1;i+k<=n&&j-k>=1;k++){
			if(mp[i+k][j]=='1'||mp[i][j-k]=='1')break;
			if(sxum[i+k][j]-sxum[i-1][j-k-1]>=1)continue;
			dp[i][j]++;
		}
		for(int k=1;i-k>=1&&j-k>=1&&i+k<=n;k++){
			if(mp[i-k][j-k]=='1'||mp[i+k][j-k]=='1')break;
			if(sumx[i+k][j-k]-sumx[i-k-1][j-k]>=1)continue;
			dp[i][j]++;
		}
		for(int k=1;i-k>=1&&j-k>=1;k++){
			if(mp[i-k][j]=='1'||mp[i][j-k]=='1')break;
			if(syum[i][j-k]-syum[i-k-1][j+1]>=1)continue;
			dp[i][j]++;
		}
		for(int k=1;i-k>=1&&j-k>=1&&j+k<=m;k++){
			if(mp[i-k][j-k]=='1'||mp[i-k][j+k]=='1')break;
			if(sumy[i-k][j+k]-sumy[i-k][j-k-1]>=1)continue;
			dp[i][j]++;
		}
		for(int k=1;i-k>=1&&j+k<=m;k++){
			if(mp[i-k][j]=='1'||mp[i][j+k]=='1')break;
			if(sxum[i][j+k]-sxum[i-k-1][j-1]>=1)continue;
			dp[i][j]++;
		}
		for(int k=1;j+k<=m&&i-k>=1&&i+k<=n;k++){
			if(mp[i-k][j+k]=='1'||mp[i+k][j+k]=='1')break;
			if(sumx[i+k][j+k]-sumx[i-k-1][j+k]>=1)continue;
			dp[i][j]++;
		}
	}

Codeforces Round #250 (Div. 2)

C:*开始贪心删周围最小的顶点,样例一直过不了,这样做是不行的。看了正解,删点实质上就是删掉这个点连接的所有边,那么就是删边,对于每一条边,肯定是删除权值较大的那个点,这样答案加的是权值小的那一个,会更优

D:这题很开心,很快就想到了从权值小到大考虑,想的是用分层图,直接用距离处理答案但没过。正解排序边

int cmp(const node& m,const node& n){
	return min(A[m.s],A[m.e])>min(A[n.s],A[n.e]);
}

然后用并查集维护联通块,并且维护联通块大小,最后得出答案(被int的n狙击了一会)

ans=2.0*ans/((n*1LL-1)*n*1.0);

Codeforces Round #251 (Div. 2)

C:发现偶数不会改变奇偶性,奇数会改变,那么前面贪心处理序列,有奇数就加进答案,有偶数就加进答案,最后的后面一定为偶数,不然就不合法

******(这一类题目发现很多都是整体考虑,拆开来会十分麻烦)*******

for(int i=1;i<=n;i++){
	sum+=A[i];cnt++;tmp.push_back(A[i]);
	if((sum&1)&&req1>0){
		//cout<<sum<<endl;
		v.push_back({cnt,tmp});
		sum=0;
		cnt=0;
		tmp.clear();
		req1--;
		last=i+1;
	}
	else if(sum%2==0&&req0>0){
		v.push_back({cnt,tmp});
		sum=0;
		cnt=0;
		tmp.clear();
		req0--;
		last=i+1;
	}
}
for(int i=last;i<=n;i++)
	v[v.size()-1].first++,v[v.size()-1].second.push_back(A[i]);

D:题意是对AB数组里的数加加减减使得A最小的大于B。然后发现这个中间值具有单峰性,三分就可以。注意答案初始值和上下界(应该是被当时的hack数据狙击了一次)

Codeforces Round #252 (Div. 2)

C:找条纹

这种顺序考虑下去就可以

D:第一次接触到了置换群(等状态好点补一下)。大致就是要想(1-n)的乱序序列恢复正常,就是n-环的个数,因为每一个环(cnt-1)次可以还原。并且有两个性质

交换一个环中任意两个元素会增加一个环***

交换不同环中任意两个元素会减少一个环***

Codeforces Round #253 (Div. 2)

C:观察本质,对两张或多张牌无法分辨他们的条件是这些牌的被点出的特性都相等,但是他们又不一样。那么观察最多(1<<10)种,直接枚举这1024,再O(n)的判断可行性

for(int i=1;i<=n;i++){
	if((sta>>X[i])&1)vis[i]|=1<<X[i];
	if((sta>>Y[i])&1)vis[i]|=1<<Y[i];
	for(int j=1;j<i;j++)
		if(vis[i]==vis[j]&&(X[i]!=X[j]||Y[i]!=Y[j])){//牌不一样但是这种情况下的被发现的特征却是一样的
			flg=1;
			break;
		}
}

D:sort从大到小(直觉),n^3,枚举选择的个数,枚举谁去,更新答案

Codeforces Round #254 (Div. 2)

C:最大密度诱导子图只有两个顶点 http://www.cnblogs.com/zzy19961112/p/5690901.html

D:这场的D太有灵性了,我觉得会很少碰到就没有看,就把E写了。

E是双标记线段树,维护了颜色,值,除了数组开小了1A还是很开心的

void update(int t,int l,int r,int L,int R,LL x){
	if(L<=l&&r<=R&&flgcol[t]!=-1){//***************************
		tr[t]+=(1LL*abs(x-flgcol[t])*(r-l+1));
		flgadd[t]+=(1LL*abs(x-flgcol[t]));
		flgcol[t]=x;
		return ;
	}

Codeforces Round #256 (Div. 2)

C:给了一些高度的栏杆,可以横着刷完或者竖着刷,求最小。区间dp或者分治(差不多),对于一个区间,要么全横着刷要么全竖着刷,因为都考虑的话太烦不像C题。

D:给了n*m的乘法表求第k大。发现了一个数出现次数是约数的个数,然后开始想从1开始往后算个数,以为这样会增长的很快,还是太天真。正解是二分这个值,check如下

int check(LL v){
	LL cnt=0;
	for(int i=1;i<=n;i++){
		cnt+=min(v/i,m*1LL);//会超出m 要取小
		if(cnt>=k)return 1;
	}
	return cnt>=k;
}

Codeforces Round #257 (Div. 2)

C:给定n*m的方格矩形,切k刀使得最小的矩形面积最大。根据直觉,小于m-1一定在m那条边上切,大于的话,切完了m-1,在n那条边上均匀地切。wa了,两种情况都考虑了一遍就过了

if(k>n+m-2)return 0*printf("-1\n");
if(n>m)swap(n,m); 
if(k<=m-1){
	LL x=m/(k+1);
	LL y=n/(k+1);
	printf("%I64d\n",max(x*n,y*m));
}
else {
	LL x=1;
	LL y=n/(k-m+1+1);
	LL xx=m/(k-n+2);
	LL yy=1;
	printf("%I64d\n",max(xx*yy,x*y));
}

D:思路没问题,wa了状态太差还没调过来。所有边建图,判断哪些边是最短路上的边,并且如果到一条铁路的顶点不止那一条路也是可以删的,做下去就可以

Codeforces Round #258 (Div. 2)

C:n k d1 d2,使得三支队伍赢得场次相等。d1,d2是绝对值,枚举这四种情况。n一定为3的倍数,然后列下方程推一下就可以

if(check(n,k,d1,d2))puts("yes");
else if(check(n,k,-d1,d2))puts("yes");
else if(check(n,k,d1,-d2))puts("yes");
else if(check(n,k,-d1,-d2))puts("yes");
else puts("no");

D:只有ab的字符串,相邻的a或者b在判断的时候可以缩在一起,统计长度为奇数和偶数的合法串数目。对于一个串只要满足边界相等中间一定满足,因为中间要么是aba或者bab这样类推(因为相邻的相等可以缩在一起)







 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值