【19.10.2】NOIP2018 模拟赛

时间:2019-10-02
本文章向大家介绍【19.10.2】NOIP2018 模拟赛,主要包括【19.10.2】NOIP2018 模拟赛使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

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

原文地址:https://www.cnblogs.com/jian-song/p/11617613.html