Codeforces Round #442 (Div. 2) 题解(877A~F)

来源:互联网 发布:本地pubmed数据库 编辑:程序博客网 时间:2024/06/06 14:08

比赛传送门
前几天并没有打这场比赛,但是听说这场题目蛮水的(我的一位初三dalao朋友直接AK了),所以昨天打了打Virtual participation
先上题解:
A:
直接暴力枚举匹配即可,就是我写得麻烦了一点

#include <cstdio>#include <algorithm>#include <cmath>#include <cstring>#include <iostream>#include <ctime>#include <map>#include <queue>#include <cstdlib>#include <string>#include <climits>#include <set>#include <vector>using namespace std;inline int read(){    int k=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){k=k*10+ch-'0';ch=getchar();}    return k*f;}char s[100010];int n;const char c[5][7]={' ','D','a','n','i','l',' ',' ','O','l','y','a',' ',' ',' ','S','l','a','v','a',' ',' ','A','n','n',' ',' ',' ',' ','N','i','k','i','t','a'};const int l[5]={5,4,5,3,6};inline bool check(int x){    bool flag;    for(int i=0;i<5;i++){        flag=1;if(x+l[i]-1<=n){            for(int j=1;j<=l[i];j++)if(s[x+j-1]!=c[i][j])flag=0;            if(flag)return 1;        }    }    return 0;}int main(){    scanf("%s",s+1);    int cnt=0;n=strlen(s+1);    for(int i=1;i<=n;i++)if(check(i))cnt++;    puts(cnt==1?"YES":"NO");}

B:
维护下a和b的前缀和,枚举两个交界点就可以了
代码比前一题好写到不知道哪里去了,但是很容易fst啊特别是交界点的特判(还好我打的是vp否则也跟着GG了

#include <cstdio>#include <algorithm>#include <cmath>#include <cstring>#include <iostream>#include <ctime>#include <map>#include <queue>#include <cstdlib>#include <string>#include <climits>#include <set>#include <vector>using namespace std;inline int read(){    int k=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){k=k*10+ch-'0';ch=getchar();}    return k*f;}char a[100010];int s[100010],t[100010];int main(){    scanf("%s",a+1);int n=strlen(a+1);    for(int i=1;i<=n;i++){        s[i]=s[i-1];t[i]=t[i-1];        if(a[i]=='a')s[i]++;else t[i]++;    }    int ans=0;    for(int i=1;i<=n;i++)        for(int j=i;j<=n+1;j++){            int p=s[i-1]+s[n]-s[j-1]+t[j-1]-t[i-1];//这里哪些要-1哪些不用就是fst的主要原因辣            ans=max(ans,p);        }    printf("%d",ans);}

C:
考虑到每个坦克只会移动到相邻两格,所以我们直接按照偶数格奇数格偶数格的顺序扫过去炸一下就好了(这样次数是最少的)
证明:显然成立(因为移动到相邻两格)

#include <cstdio>#include <algorithm>#include <cmath>#include <cstring>#include <iostream>#include <ctime>#include <map>#include <queue>#include <cstdlib>#include <string>#include <climits>#include <set>#include <vector>using namespace std;inline int read(){    int k=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){k=k*10+ch-'0';ch=getchar();}    return k*f;}int main(){    int n=read();    printf("%d\n",n/2*2+(n-n/2));    for(int i=2;i<=n;i+=2)printf("%d ",i);    for(int i=1;i<=n;i+=2)printf("%d ",i);    for(int i=2;i<=n;i+=2)printf("%d ",i);}

D:
大力BFS,我们把每一个点记录一下走到的最少步数,然后向四个方向不断更新节点,但是要保证每个点只会进队一次,否则会MLE
时间复杂度O(
然后就过了23333???
后来看了一下官方题解做法很神奇(我看不懂),不过底下的讨论也给出了很多的不同BFS做法。。。亦可赛艇

#include <cstdio>#include <algorithm>#include <cmath>#include <cstring>#include <iostream>#include <ctime>#include <map>#include <queue>#include <cstdlib>#include <string>#include <climits>#include <set>#include <vector>#define pa pair<int,int>#define mp make_pair#define fi first#define se secondusing namespace std;inline int read(){    int k=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){k=k*10+ch-'0';ch=getchar();}    return k*f;}const int dx[4]={1,-1,0,0};const int dy[4]={0,0,1,-1};int n,m,k,sx,sy,tx,ty;char s[1010][1010];int dist[1010][1010];bool vis[1010][1010];queue<pa>q;int main(){    memset(dist,-1,sizeof dist);    n=read();m=read();k=read();    for(int i=1;i<=n;i++)scanf("%s",s[i]+1);    sx=read();sy=read();q.push(mp(sx,sy));dist[sx][sy]=0;vis[sx][sy]=1;    while(!q.empty()){        pa now=q.front();q.pop();        for(int i=0;i<4;i++){            int x=now.fi+dx[i],y=now.se+dy[i];int cnt=1;            while(x>0&&x<=n&&y>0&&y<=m&&s[x][y]!='#'&&(dist[x][y]==-1||dist[x][y]==dist[now.fi][now.se]+1)&&cnt<=k){                dist[x][y]=dist[now.fi][now.se]+1;                if(!vis[x][y])q.push(pa(x,y));vis[x][y]=1;                cnt++;x+=dx[i];y+=dy[i];            }        }    }    int tx=read(),ty=read();    printf("%d",dist[tx][ty]);    return 0;}

E:
线段树维护dfs序区间修改裸题
打区间翻转(就是xor)的lazy标记就可以了
这种题显然是数据结构啊,看到这种子树的修改和询问操作想都不用想直接上就可以了
时间复杂度O(nlogn)

#include <cstdio>#include <algorithm>#include <cmath>#include <cstring>#include <iostream>#include <ctime>#include <map>#include <queue>#include <cstdlib>#include <string>#include <climits>#include <set>#include <vector>using namespace std;inline int read(){    int k=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){k=k*10+ch-'0';ch=getchar();}    return k*f;}int nedge=0,p[200010],nex[200010],head[200010];int n,t[800010],add[800010],lt[800010],rt[800010];int sx[200010],cnt=0,a[200010],s[200010],xs[200010];inline void addedge(int a,int b){    p[++nedge]=b;nex[nedge]=head[a];head[a]=nedge;}inline void dfs(int x){    sx[x]=++cnt;xs[cnt]=x;s[x]=1;    for(int k=head[x];k;k=nex[k]){        dfs(p[k]);s[x]+=s[p[k]];    }}inline void pushdown(int nod){    if(!add[nod])return;    if(lt[nod]!=rt[nod]){        add[nod*2]^=1;add[nod*2+1]^=1;        t[nod*2]=rt[nod*2]-lt[nod*2]+1-t[nod*2];        t[nod*2+1]=rt[nod*2+1]-lt[nod*2+1]+1-t[nod*2+1];    }add[nod]=0;}inline void build(int l,int r,int nod){    lt[nod]=l;rt[nod]=r;    if(l==r){t[nod]=a[xs[l]];return;}    int mid=l+r>>1;    build(l,mid,nod*2);build(mid+1,r,nod*2+1);    t[nod]=t[nod*2]+t[nod*2+1];}inline void xg(int i,int j,int nod){    pushdown(nod);    if(lt[nod]>=i&&rt[nod]<=j){        t[nod]=rt[nod]-lt[nod]+1-t[nod];add[nod]=1;        return;    }    int mid=lt[nod]+rt[nod]>>1;    if(i<=mid)xg(i,j,nod*2);    if(j>mid)xg(i,j,nod*2+1);    t[nod]=t[nod*2]+t[nod*2+1];}inline int ssum(int i,int j,int nod){    pushdown(nod);    if(lt[nod]>=i&&rt[nod]<=j)return t[nod];    int mid=lt[nod]+rt[nod]>>1,ans=0;    if(i<=mid)ans+=ssum(i,j,nod*2);    if(j>mid)ans+=ssum(i,j,nod*2+1);    return ans;}int main(){    n=read();    for(int i=2;i<=n;i++){        int x=read();        addedge(x,i);    }    dfs(1);    for(int i=1;i<=n;i++)a[i]=read();    build(1,n,1);    int m=read();    for(int i=1;i<=m;i++){        char c[5];scanf("%s",c);int x=read();        if(c[0]=='g')printf("%d\n",ssum(sx[x],sx[x]+s[x]-1,1));        else xg(sx[x],sx[x]+s[x]-1,1);    }    return 0;}

F:
最后一题有点意思,我并没有在两个小时的时间内写出来
%%%wzp(就是那位AKdalao)考场直接切掉了orz
这题是怎么被wzp想到莫队的啊QAQ

我们考虑对这整个数列做前缀和(种类和数量,具体看代码),记为s[i]。那么对于一个区间[l,r],在该区间内的i对答案的贡献就是在区间内的前缀和=s[i]k的个数+前缀和=s[i]+k的个数
那么我们对于每个询问都这么暴力地去算,然后用莫队搞一下就好了
关键不在莫队,因为数值范围很大,所以我们需要对s[i]进行离散化处理,所以会麻烦很多。。。
时间复杂度貌似是O(nn+nlogn)
那就贴代码吧(WA了n发才过的QAQ)

#include <cstdio>#include <algorithm>#include <cmath>#include <cstring>#include <iostream>#include <ctime>#include <map>#include <queue>#include <cstdlib>#include <string>#include <climits>#include <set>#include <vector>#define int long longusing namespace std;inline int read(){    int k=0,f=1;char ch=getchar();    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while(ch>='0'&&ch<='9'){k=k*10+ch-'0';ch=getchar();}    return k*f;}struct ppap{int x,y,p,w;}qq[100010];inline bool cmp(ppap a,ppap b){return a.p==b.p?a.y<b.y:a.p<b.p;}int a[100010],s[100010],b[100010],q[100010],h[100010],now,n,k,ans[100010];inline void add1(int x){    if(h[x])now+=a[h[x]];a[s[x]]++;}inline void add2(int x){    if(q[x])now+=a[q[x]];a[s[x]]++;}inline void jian1(int x){    a[s[x]]--;if(h[x])now-=a[h[x]];}inline void jian2(int x){    a[s[x]]--;if(q[x])now-=a[q[x]];}signed main(){    n=read();k=read();    for(int i=1;i<=n;i++)a[i]=read();    for(int i=1;i<=n;i++){        int x=read();        s[i]=s[i-1]+((a[i]==1)?x:-x);//前缀和:第一种书+,第二种书-        b[i]=s[i];    }    sort(b+1,b+n+1);int cnt=unique(b+1,b+n+1)-b-1;    int K=lower_bound(b+1,b+cnt+1,k)-b;if(b[K]!=k)K=0;    for(int i=1;i<=n;i++){        q[i]=lower_bound(b+1,b+cnt+1,s[i]-k)-b;//s[i]-k        if(b[q[i]]!=s[i]-k)q[i]=0;        h[i]=lower_bound(b+1,b+cnt+1,s[i]+k)-b;//s[i]+k        if(b[h[i]]!=s[i]+k)h[i]=0;    }    for(int i=1;i<=n;i++)s[i]=lower_bound(b+1,b+cnt+1,s[i])-b;    h[0]=K;//以上全为离散化    int Q=read();int P=sqrt(n);    for(int i=1;i<=Q;i++){        qq[i].x=read();qq[i].y=read();        qq[i].p=(qq[i].x-1)/P+1;qq[i].w=i;    }    sort(qq+1,qq+Q+1,cmp);    memset(a,0,sizeof a);    int l=1,r=0;now=0;    for(int i=1;i<=Q;i++){        while(l<qq[i].x)jian1(l++);        while(l>qq[i].x)add1(--l);        while(r<qq[i].y)add2(++r);        while(r>qq[i].y)jian2(r--);        ans[qq[i].w]=now+a[h[l-1]];//因为是前缀和,有些以l为开头的合法区间要用l-1来算    }    for(int i=1;i<=Q;i++)printf("%lld\n",ans[i]);    return 0;}

总结:
这场题目算是比较水的了(至少对于前5题来说),题目也就简单的思路题和算法模板题了,F题比较有趣,考验思维和代码能力
(按照前5题的水平)关键是noip不会出这种大水题吧。。。
看来我还是得去做更难的题了

原创粉丝点击