在第一次CF第一题被叉与第二次CF第一题被FST掉的阴影下,报名参加了第三次的CF。。。心路历程大概是这样的:
开场后15min内:wocCF怎么上不上去?。。。算了到CF群里开始看起了T1题面,慌乱之中又看漏了T1中的正整数,成功掉了50分。。。
终于在21min过了T1,开始看T2
1min后:嗯这道题怎么好像很简单啊。。。4min左右写完,然后网卡,交不了。。。
然后大概在26min左右看起了T3:这道题不是傻逼DP吗?写完把细节改了改,35min左右过了所有样例,然而。。。woc怎么还是交不了?
于是看起T4。。。到了41min,啊终于能交了,于是在41min同时过了T2与T3
剩下的2h-41min=1h19min:一直T4BFS不会写,非要写BFS套BFS还傻逼地以为写的非常是正解,看到TLE后还加了一个类似分层图的优化更加觉得是正解了,但这并不妨碍我从头T到尾。。。
然后、、比赛结束,以rank1300多强势垫底
认真地总结一下吧:CF每次都发挥不出水平,主要是因为CF的按时间减分的赛制让我每次都非常紧张,迫切地想把每一题都迅速做完,然后每题就不敢多想,自然也就不能发挥出来了。所以既然考场上没来得及想,就在考完试后再多想想吧。其次的话,自己水平还是不行吧,如果水平够的话也不会那么紧张,所以还是需要多加训练。话不多说,开始说题
A. Salem and Sticks
暴力枚举,三种情况取个min即可
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstdlib>
using namespace std;
const int N=1e3+10;
const int INF=1e9;
int n,a[N],t,ans=INF;
int main(){
scanf("%d",&n);
for(register int i=1;i<=n;i++)scanf("%d",&a[i]);
for(register int p=1;p<=100;p++){
int sum=0;
for(register int i=1;i<=n;i++)
sum+=min(abs(a[i]-p),min(abs(a[i]-1-p),abs(a[i]+1-p)));
if(sum<ans)ans=sum,t=p;
}
printf("%d %d\n",t,ans);
return 0;
}
B. Zuhair and Strings
还是暴力枚举
#include<cstdio>
#include<iostream>
using namespace std;
const int N=2e5+10;
int n,k,sum,ans,now;
char s[N];
int main(){
scanf("%d%d",&n,&k);
scanf("%s",s+1);
for(register int i=0;i<26;i++){
sum=now=0;
for(register int j=1;j<=n;j++){
if(s[j]==i+'a')now++;else now=0;
if(now==k)sum++,now=0;
}
ans=max(ans,sum);
}
printf("%d\n",ans);
return 0;
}
C. Ayoub and Lost Array
设\(dp[i][j]\)表示前 \(i\) 个数和在模 \(3\) 意义下为 \(j\) 的方案数,然后枚举 \(l,r\) 转移,复杂度 \(O(3n(r-l))\)
考虑把 \(l-r\) 中的数放在模 \(3\) 的剩余系下处理,这样复杂度就只有 \(O(9n)\) 了
#include<cstdio>
#include<iostream>
using namespace std;
const int N=2e5+10;
const int mod=1e9+7;
int n,l,r,dp[N][3],num[3];
inline void Add(int &x,int y){x+=y;x-=x>=mod? mod:0;}
int main(){
scanf("%d%d%d",&n,&l,&r);
num[0]=r/3+1;
if(r-1>=0)num[1]=(r-1)/3+1;
if(r-2>=0)num[2]=(r-2)/3+1;
if(l-1>=0)num[0]-=max((l-1)/3+1,0);
if(l-2>=0)num[1]-=max((l-2)/3+1,0);
if(l-3>=0)num[2]-=max((l-3)/3+1,0);
dp[0][0]=1;
for(register int i=1;i<=n;i++)
for(register int j=0;j<3;j++)
for(register int p=0;p<3;p++)
Add(dp[i][j],1ll*dp[i-1][(j-p+3)%3]*num[p]%mod);
printf("%d\n",dp[n][0]);
return 0;
}
D. Kilani and the Game
也就是个简单的BFS,注意访问过的点直接打上vis标记不再访问即可
同一种颜色的点同时BFS,不要每个单独BFS(我当时就是这样T掉的)
#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int N=1e3+10;
const int M=20;
int n,m,p,s[N],head[M],cnt,num[N][N],ans[M];
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
bool vis[N][N];
char mp[N][N];
struct peo{int nxt,x,y;}P[N*N];
struct node{int lef,x,y,col;node(int leff=0,int xx=0,int yy=0,int coll=0){lef=leff;x=xx;y=yy;col=coll;}};
queue<node>q;
inline void Add(int po,int x,int y){P[++cnt].nxt=head[po];P[cnt].x=x;P[cnt].y=y;head[po]=cnt;}
inline void exBFS(node k){
queue<node>nq;nq.push(k);
while(!q.empty()&&q.front().col==k.col)nq.push(q.front()),vis[q.front().x][q.front().y]=true,q.pop();
vis[k.x][k.y]=true;
while(!nq.empty()){
node u=nq.front();nq.pop();
for(register int i=0;i<4;i++){
int nx=u.x+dx[i],ny=u.y+dy[i];
if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&mp[nx][ny]!='#'&&(num[nx][ny]==0||num[nx][ny]==u.col)){
num[nx][ny]=u.col;
if(!vis[nx][ny]){
vis[nx][ny]=true;
if(u.lef>1)nq.push(node(u.lef-1,nx,ny,u.col));
else q.push(node(s[u.col],nx,ny,u.col));
}
}
}
}
}
inline void BFS(){
while(!q.empty()){node u=q.front();q.pop();exBFS(u);}
}
int main(){
scanf("%d%d%d",&n,&m,&p);
for(register int i=1;i<=p;i++)scanf("%d",&s[i]);
for(register int i=1;i<=n;i++)scanf("%s",mp[i]+1);
for(register int i=1;i<=n;i++)
for(register int j=1;j<=m;j++)
if(mp[i][j]!='#'&&mp[i][j]!='.')
Add(mp[i][j]-'0',i,j),num[i][j]=mp[i][j]-'0';
for(register int i=1;i<=p;i++)
for(register int j=head[i];j;j=P[j].nxt)
q.push(node(s[i],P[j].x,P[j].y,i)),vis[P[j].x][P[j].y]=true;
BFS();
for(register int i=1;i<=n;i++)
for(register int j=1;j<=m;j++)
ans[num[i][j]]++;
for(register int i=1;i<=p;i++)
printf("%d%c",ans[i],i==p? '\n':' ');
return 0;
}
E. Helping Hiasat
因为一个组中的人一定是相互排斥的,所以在一个组中的人中两两建边,表示这两个人不能同时选,然后建出来的图的最大独立集的点数就是最多的愉悦人数。
那么怎么求无向图的最大独立集呢?有一个性质:一个无向图的最大独立集等于这个无向图补图的最大团。
这个性质可以这么理解:对于原图,如果两个点之间有边,那么在补图中一定没有这条边,而最大团要求所选取的点集中的点两两都有边相连,那么这两个点就不可能同在补图的最大团中,也就相应的不可能同在原图的最大独立集中了
然后最大团的话,多随机几下加点顺序取个最大值就行了
#include<cstdio>
#include<iostream>
#include<map>
#include<cstring>
#include<bitset>
#include<algorithm>
using namespace std;
const int N=50;
int n,m,k,store[N],a[N],tot,T=5e4;
bool Walk[N][N];
string s;
map<string,int>mp;
bitset<N>sav,ans;
inline void Process1(){
for(register int i=1;i<=store[0];i++)
for(register int j=i+1;j<=store[0];j++)
Walk[store[i]][store[j]]=Walk[store[j]][store[i]]=false;
memset(store,0,sizeof(store));
}
inline void Process2(){
if(!mp.count(s))mp[s]=++tot;
store[++store[0]]=mp[s];
}
int main(){
scanf("%d%d",&n,&m);scanf("%d",&k);n--;
memset(Walk,true,sizeof(Walk));
while(n--){
scanf("%d",&k);
if(k==1)Process1();
else cin>>s,Process2();
}
Process1();
for(register int i=1;i<=m;i++)a[i]=i;
while(T--){
random_shuffle(a+1,a+m+1);
for(register int i=1;i<=m;i++)sav[i]=1;
for(register int i=1;i<=m;i++)
if(sav[a[i]]==1)
for(register int j=i+1;j<=m;j++)
if(!Walk[a[i]][a[j]])sav[a[j]]=0;
if(sav.count()>ans.count())ans=sav;
}
printf("%d\n",ans.count());
return 0;
}
1月31号的codeforces再战!(立flag:下次一定要到深蓝)