zoukankan      html  css  js  c++  java
  • 【2019.10.2】NOIP2018 模拟赛

    总结:

    实话除了T2之外T1、T3都还蛮可做的,操作比较常规并不难想,T2的暴力虽然拿分少但也可以做。所以还是要抓紧时间多做点题多敲点代码,不然就会知道大概做法但推不出正解。

    多做题,多考试,多总结。

     位运算 + 联通块 + KMP + 博弈论。

    T1.

    题意:

    给出一个n个数的序列a,每次操作可以将a中一个数变成整个序列的值的异或。求最少需要多少次才能将a变成目标序列b,无法完成输出-1。

    题解:

    异或题(位运算题)都不会是纯模拟,所以考完多做点这种题练练手感熟悉操作。

    一个性质:一个数异或同一个数两次后还是原来的数,证明枚举一下就行。

    根据这个性质我们可以看出题其实目就是一个n+1的序列交换顺序,达到期望序列,不然输出-1。

    交换过程把不相等的a[i]和b[i]连成边,交换次数即联通块边数。

    跳到不同联通块次数要加1,n+1不同的话单独算一个连通块。

    不难发现最后答案就是:连通块数+边数-1,因为n+1不算操作(否则重复一次操作),但我们在计算的时候算上了它。

    代码:

    #include<bits/stdc++.h>
    #define For(i,l,r) for(int i=l;i<=r;i++)
    #define ll long long
    using namespace std;
    const int M=1e5+5;
    int n,a[M],b[M],x[M],y[M],vis[M],cnt,ans;
    map<int,int>d;
    vector<int>Q[M];
    inline int read(){
        int f=1,sum=0;
        char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){sum=(sum<<1)+(sum<<3)+(ch^48);ch=getchar();}
        return f*sum; 
    }
    inline void dfs(int x){
        vis[x]=1;
        For(i,0,Q[x].size()-1){
            if(!vis[Q[x][i]]){
                dfs(Q[x][i]);
            }
        }
    }
    int main(){
        n=read();
        For(i,1,n){
            a[i]=read();a[n+1]^=a[i];
            if(!d[a[i]]) d[a[i]]=++cnt;
        }
        if(!d[a[n+1]]) d[a[n+1]]=++cnt;
        For(i,1,n){
            b[i]=read();b[n+1]^=b[i];
            if(!d[b[i]]) d[b[i]]=++cnt;
        }
        if(!d[b[n+1]]) d[b[n+1]]=++cnt;
        memcpy(x,a,sizeof(x));memcpy(y,b,sizeof(y));
        sort(x+1,x+n+2);sort(y+1,y+n+2);
        For(i,1,n) if(x[i]!=y[i]){printf("-1");return 0;}
        For(i,1,n+1) a[i]=d[a[i]],b[i]=d[b[i]];
        For(i,1,n) if(a[i]!=b[i]) ans++,Q[a[i]].push_back(b[i]);
        if(a[n+1]!=b[n+1]) Q[a[n+1]].push_back(b[n+1]);
        For(i,1,cnt) if(Q[i].size()&&!vis[i]) dfs(i),ans++;
        printf("%d",ans-1);
        return 0;
    }
    View Code

    T2.

    题意:给出一棵树,每个节点有两个值Ai,Di,表示这个节点能给它的0~Di级祖先的F贡献Ai。然后给每条边一个出现概率,每次询问某个节点的联通块F值的和的平方的期望。

    题解:

    代码:

    T3.

    题意:

    给定两个串ST|S| >= |T|两个人博弈,每一轮操作,两人先后可以删掉S的第一位或最后一位。当操作以后的串的长度等于|T|时,游戏停止。如果停止时S剩下的串=T后手获胜,否则先手获胜。两人均采取最策略下谁赢。

    题解:

    考虑简化表示状态

    设左边已经删掉了L字符,右边已经删掉了R字符,那么用R-L来表示当前状态。可以用KMP求出有哪些目标状态。

    一开始R-L0,每一次操作可以让它+1或者-1,双方轮流操作,总共|S|-|T|次操作,双方都用最优策略看最后是否能到达目标状态。

    显然所有的目标状态奇偶性相同

    |S|-|T|为奇数时,最后一次操作是先手做,它肯定往不是目标状态走,那么一个位置在最后一次操作前是目标状态当且仅当它+1-1都是目标状态。

    现在就全部转化成|S|-|T|为偶数的情况,假如0是目标状态,那么显然后手会赢,因为无论先手往哪里走后手都可以把他拉回来,如果0不是,并且-22不全是,那么先手一定会朝不是的那一边走,后手无论如何都没有办法将它拉回到是的那一边了。因此后手会赢当且仅当0是目标状态或者-22都是目标状态否则都是先手赢。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int M=1e5+5;
    int t,la,lb,nxt[M],goal[M<<1],pos[M];
    char a[M],b[M];
    int main(){
        scanf("%d",&t);
        while(t--){
            scanf("%s",a+1),scanf("%s",b+1);
            la=strlen(a+1),lb=strlen(b+1);
            memset(nxt,0,sizeof(nxt));
            memset(goal,0,sizeof(goal));
            memset(pos,0,sizeof(pos));
            for(int i=2,j=0;i<=lb;i++){
                while(j&&b[j+1]!=b[i]) j=nxt[j];
                if(b[j+1]==b[i]) j++;
                nxt[i]=j;
            }
            for(int i=1,j=0;i<=la;i++){
                while(j&&(b[j+1]!=a[i]||j==lb)) j=nxt[j];
                if(b[j+1]==a[i]) j++;
                if(j==lb) pos[i]=1;
            }
            if((la-lb)&1) for(int i=1;i<=la;i++) goal[la+lb-2*i+1+la]=pos[i-1]&pos[i];
            else for(int i=1;i<=la;i++) goal[la+lb-2*i+la]=pos[i];
            if(goal[la]||goal[la-2]&&goal[la+2]) printf("pty
    ");
            else printf("cqf
    ");
        }
        return 0;
    } 
    View Code
  • 相关阅读:
    用友U8 | 【存货管理】提示用户***正在记账,不允许并发。
    用友U8 | 怎么准确查找【采购入库单】、【采购发票】,对应的凭证号?
    用友U8 | 中途启用序列号管理,该怎么操作?
    Excel:提取身份证号中的性别
    给jupyter 添加代码自动补全功能
    SQL函数之:截断字符串
    解决Maven子项目提示 ‘parent.relativePath‘ of POM
    公共NTP资源汇总
    iperf3的使用
    ZeroTier的使用
  • 原文地址:https://www.cnblogs.com/jian-song/p/11617613.html
Copyright © 2011-2022 走看看