【NOIP2016提高A组模拟7.21】Double-row

来源:互联网 发布:java程序员兼职 编辑:程序博客网 时间:2024/06/06 03:43

Description

科学家温斯顿在一张超长的白纸上写下了两行数,每一行数有N个。
但他写完后觉得看起来有点不和谐。他希望重新编排,使得每一行数中没有相同的数。
他每次可以调换同一列的两个数。
请帮他找到操作次数最少的方案。

Input

第一行一个正整数N,代表每一行数的个数。
第二第三行每行N个数,代表第一行与第二行的数值。

Output

第一行一个整数,表示最少的操作次数。数据保证合法的操作是存在的。

Sample Input

9
2 5 5 2 7 4 7 3 9
1 6 8 4 6 3 9 1 8

Sample Output

3

Data Constraint

设字符串的长度为N。
Subtask1[30pts]:N<=500
Subtask2[30pts]:N<=5000
Subtask3[40pts]:N<=50000
数值Xi满足1<=X<=100000

Solution

这题的方法比较巧妙
可以发现,每个数字最多只能出现两次,否则就会无解
如果两个数字出在同一行,那么这两列必须且仅能交换一列
如果两个数字出现在不同行,那么这两列要么同时换,要么同时不换
把每一列的两个数字看成一个点,分别以上述两种方式连两种边,这样会形成一堆联通块,每个联通块之间相互独立互不影响。
对于每个联通块,选定一个点,让它交换或不交换,那么可以通过一个点得知整个联通块需要交换的点的个数,这不是答案,答案还可能是在这种情况下交换的点不交换,不交换的点交换,所以还要取个min

Code

#include<cstdio>#include<algorithm>#include<cstring>#define fo(i,a,b) for(int i=a;i<=b;i++)#define N 101000using namespace std;int t[2][N][2],ans=0,n,last[N],next[N*10],to[N*10],date[N*10],tot=0,bz[N],an,na;void put(int x,int y,int z){    next[++tot]=last[x];last[x]=tot;to[tot]=y;date[tot]=z;    next[++tot]=last[y];last[y]=tot;to[tot]=x;date[tot]=z;}void dg(int x,int z){    na++;bz[x]=z;if(z==1) an++;    for(int i=last[x];i;i=next[i])    if(bz[to[i]]==-1)    {        if(date[i]==0) dg(to[i],1-z);        else dg(to[i],z);    }}int main(){    scanf("%d",&n);    memset(bz,255,sizeof(bz));    fo(i,1,n)    {        int x;scanf("%d",&x);        if(t[0][x][0]==0) t[0][x][0]=i;else t[0][x][1]=i;    }    fo(i,1,n)    {        int x;scanf("%d",&x);        if(t[1][x][0]==0) t[1][x][0]=i;else t[1][x][1]=i;    }    fo(x,1,100000)    if(t[0][x][0]||t[1][x][0])    {        if(t[0][x][0])        {            if(t[0][x][1]) put(t[0][x][0],t[0][x][1],0);            else if(t[1][x][0]) put(t[0][x][0],t[1][x][0],1);        }        else        if(t[1][x][0])        {            if(t[1][x][1]) put(t[1][x][0],t[1][x][1],0);        }    }    fo(i,1,n)    if(bz[i]==-1)    {        an=na=0;        dg(i,0);        ans+=min(an,na-an);    }    printf("%d",ans);}
原创粉丝点击