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

比赛传送门
前几天并没有打这场比赛,但是听说这场题目蛮水的(我的一位初三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 second
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;
}
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 long
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;
}
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不会出这种大水题吧。。。
看来我还是得去做更难的题了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值