心得
好久不做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个数代表长度为的木棒有个,
问最多能构成多少个三角形
题解
从小到大考虑这个问题,
如果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;
}