Codeforces Round #533 (Div. 2)比赛总结

在第一次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:下次一定要到深蓝)

转载于:https://www.cnblogs.com/ForwardFuture/p/10296662.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值