Codeforces Global Round 2(C、D、E)

心得

好久不做cf,今天做cf迷之慢……

C题是个找规律题,结果写了一个大长dfs搞过去了……

本来不会做D,一看马老师5题了,

结束前最后10min急中生智才想出来D怎么做

然而敲完25min,debug又10min,我枯了

E题这种题也很难判断是贪心还是dp,

之前做过对子和顺子的贪心,也做过camp的围三角形暴力dfs,也做过round1的jongmah的dp

补就完事了.jpg

C.Ramesses and Corner Inversion(找规律)

题目

给两个n*m(n,m<=500)的仅由0和1组成的矩形a和矩形b,

问能不能通过无限次翻转一个矩形的四个顶点,

即使0变成1,1变成0,矩形规模>=2*2,四个顶点必须落在a矩形内

使得a矩形和b矩形完全一致

题解

Solution1:可以数一下每一行每一列不同数的个数,如果有奇数就不能翻成一致的,否则就能

证明什么的口胡一下:

首先,证明奇数的一定不行

如果当前不一样的每一行的值和每一列的值都是偶数,

那么我们进行依次翻转操作会翻掉一个2*2,会使两个行的不一样的值减2,两个列的不一样的值减2

而最后的终止状态是全0的偶数态,所以当且仅当由偶数态转移而来

再次,证明偶数的一定可以

如果全偶的话,可以通过有限的翻转把所有都压到左上角,

类似一个2p*2q的棋盘,然后相邻的四个四个翻就好了

1 1 0 1 1
1 1 0 1 1
1 0 1 1 1
1 0 1 1 1

像这种不相邻的,可以通过翻转,使得1都堆到左上角

Solution2:每次暴力翻邻近的四个,可以证明翻邻近的和翻远处的,效果是等价的

我为什么要dfs啊,tm感觉自己还写得挺6……

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=505; 
int n,m;
int a[maxn][maxn],b[maxn][maxn]; 
int now;
bool flag;
bool change(int x,int y)
{
	//printf("change:(%d,%d)\n",x,y);
	int yy=y+1,xx=x+1;
	if(yy>m||xx>n)return 0;
	while(yy<=m&&b[x][yy]!=1)yy++;
	while(xx<=n&&b[xx][y]!=1)xx++;
	if(yy>m&&xx>n)return 0;
	if(xx<=n&&yy<=m)
	{
		b[x][y]=0;
	    b[x][yy]=0;
	    b[xx][y]=0;
	    now-=3; 
		if(b[xx][yy]==1)b[xx][yy]=0,now--;
		else b[xx][yy]=1,now++;
		//printf("(%d,%d),(%d,%d),(%d,%d),(%d,%d)\n",x,y,x,yy,xx,y,xx,yy);
		return 1;
	}
	if(xx<=n)
	{
		yy=y+1;
		while(yy<=m&&b[xx][yy]!=1)yy++;
		if(yy>m)return 0;
		b[x][y]=0;
	    b[xx][y]=0;
	    b[xx][yy]=0;
	    now-=3;
	    if(b[x][yy])b[x][yy]=0,now--;
	    else b[x][yy]=1,now++;
	    //printf("(%d,%d),(%d,%d),(%d,%d),(%d,%d)\n",x,y,x,yy,xx,y,xx,yy);
	    return 1;
	}
	if(yy<=m)
	{
		xx=x+1;
		while(xx<=n&&b[xx][yy]!=1)xx++;
		if(xx>n)return 0;
		b[x][y]=0;
	    b[x][yy]=0;
	    b[xx][yy]=0;
	    now-=3;
	    if(b[xx][y])b[xx][y]=0,now--;
	    else b[xx][y]=1,now++;
	    //printf("(%d,%d),(%d,%d),(%d,%d),(%d,%d)\n",x,y,x,yy,xx,y,xx,yy);
	    return 1;
	}
	return 0;
}
void dfs(int x,int y)
{
	if(!flag)return;//无解 
	if(now==0)return;//已解 
	if(x==n&&y==m)
	{
		if(now>0)flag=0;
		return;
	}
	if(b[x][y]==1)
	{
	 if(!change(x,y))
	 {
	  flag=0;
	  return;
     }
    }
	if(y==m)dfs(x+1,1);
	else dfs(x,y+1);
}
bool solve()
{
	flag=1;
	dfs(1,1);
	return flag;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
	for(int j=1;j<=m;++j)
	scanf("%d",&a[i][j]);
	for(int i=1;i<=n;++i)
	for(int j=1;j<=m;++j)
	{
	scanf("%d",&b[i][j]);
	b[i][j]^=a[i][j];
	if(b[i][j])now++;
    }
    puts(solve()?"Yes":"No");
	return 0;
} 

D.Frets On Fire(差分)

n(n<=1e5)个数,

以下每个数s1-sn,代表一个n*(1e18+1)的矩阵每行的行首

每一行后面的值依次是[s1,s1+1,s1+2,……],长度1e18+1

现在给定q(q<=1e5)个询问,每次询问[l,r],0<=l,r<=1e18,

问矩阵的这些列里不同的数字有多少个

题解

注意到[l,r]和[1,r-l+1]对答案没有影响的,所以核心在于长度

首先,差分求区间长度在哪些值的时候,对下一个长度的贡献会发生变化

也就是现在的区间追上了后来的区间

处理出这些位置的值和这些位置的当前贡献,

对待询问,可以在处理的过程中离线一并回答,

或者处理好了之后二分位置再回答

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
int n,q,tot;
ll s[maxn];
ll l,r,len;
map<ll,ll>vis,res,ans;
//res:这个区间长度对下一个区间长度的贡献
//vis:差分数组即区间长度 出现的个数
ll num[maxn],cnt;
ll out[maxn];
int main()
{
	scanf("%d",&n);
	tot=n;
	for(int i=1;i<=n;++i)
	scanf("%I64d",&s[i]);
	sort(s+1,s+n+1);
	for(int i=1;i<n;++i)
	{
		ll p=s[i+1]-s[i];
		if(!p)
		{
			tot--;
			continue;
		}
		if(!vis[p])num[++cnt]=p;
		vis[p]++;		
	}
	//for(map<ll,ll>::iterator it=vis.begin();it!=vis.end();it++)
	//printf("%I64d:%I64d\n",it->first,it->second);
	sort(num,num+cnt+1);
	num[0]=ans[0]=0;
	for(int i=1;i<=cnt;++i)
	{
		ans[num[i]]=ans[num[i-1]]+tot*(num[i]-num[i-1]);
		res[num[i-1]]=tot;
		tot-=vis[num[i]];
	}
	//printf("%I64d: tot\n",tot);
	res[num[cnt]]=tot; 
	//for(int i=0;i<=cnt;++i)
	//printf("->%I64d:%I64d + %I64d\n",num[i],ans[num[i]],res[num[i]]);
	scanf("%d",&q);
	for(int k=1;k<=q;++k)
	{
		scanf("%I64d%I64d",&l,&r);
		len=(r-l+1);
		int pos=upper_bound(num,num+cnt+1,len)-num;
		pos--;//<=的最大 
		//printf("pos:%d\n",pos);
		//printf("v:%I64d +:%I64d\n",ans[num[pos]],res[num[pos]]);
		out[k]=ans[num[pos]]+(res[num[pos]])*(len-num[pos]);
	}
	for(int k=1;k<=q;++k)
	printf("%I64d%c",out[k],k==q?'\n':' ');
	return 0;
} 

E.Pavel and Triangles(贪心)

题目

n(n<=3e5)个数,以下n个数,

第i个数a_{i}代表长度为2^{i}的木棒有a_{i}个,

问最多能构成多少个三角形

题解

从小到大考虑这个问题,

如果1不和自己凑,那么就浪费了,因为1+1==2没法和大的凑

那就三个三个先把1凑了,然后剩下一个1或者两个1

然后,注意到1+2<4,1+2>2,1+4>4

所以1只能和剩下的两个一样的凑,不能和不一样的两个凑,不然就浪费了

考虑到现在有1要被浪费,而2如果不和这个1凑,也会找一个2来凑

那么我们就把两个2和1直接凑一起,减少了浪费1,减少了消耗2,也增加了答案

递增解决即可

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+10;
typedef long long ll;
int n,a[maxn],now; 
ll ans;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	scanf("%d",&a[i]);
	for(int i=1;i<=n;++i)
	{
		ans+=a[i]/3;//先凑3个1 
		a[i]%=3;
		now=max(now,i+1);
		while(now<=n&&a[i])//尽力去凑1 2 2
		{
			if(a[now]>=2)ans++,a[now]-=2,a[i]--;
			else now++;
		}
	}
	printf("%I64d\n",ans);
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/Code92007/article/details/89061736