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

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

Hint

对于样例数据,只需调换1,3,5列即可。

Data Constraint

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

Solution

  • 可以明显得到同一个数不可能出现 3 次或以上,否则无解。

  • 如果同一个数出现在同一侧(设其出现在 X 列和 Y 列),那么 X 列与 Y 列之中有且只有一列要调换;

  • 如果同一个数出现在不同一侧(设其出现在 X 列和 Y 列),那么要么均不调换,要么均调换。

  • 我们把每一列看作一个点,这样的关系用边连起来,那么连通块内相互影响,而连通块之间
    是相互独立的。

  • 所以分别处理每一个连通块:随机确定一列换不换,那么连通块内其余的状态都会被确定。

  • 我们只需选择操作数少的即可。

Code

#include<cstdio>
using namespace std;
const int N=50001;
int n,ans,sum,tot;
int first[N],next[N<<1],en[N<<1],w[N<<1];
int a[N<<1],b[N<<1],f[N];
bool bz[N],bz1[N];
inline int read()
{
    int X=0,w=1; char ch=0;
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}
inline void insert(int x,int y,int z)
{
    next[++tot]=first[x];
    first[x]=tot;
    en[tot]=y;
    w[tot]=z;
}
inline void dfs(int x)
{
    sum+=f[x];
    bz[x]=true;
    for(int i=first[x];i;i=next[i])
        if(!bz[en[i]])
        {
            f[en[i]]=(f[x]+w[i])&1;
            dfs(en[i]);
        }
    f[x]=0;
}
inline void dfs1(int x)
{
    sum+=f[x];
    bz1[x]=true;
    for(int i=first[x];i;i=next[i])
        if(!bz1[en[i]])
        {
            f[en[i]]=(f[x]+w[i])&1;
            dfs1(en[i]);
        }
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        int x=read();
        if(!a[x]) a[x]=i; else
        {
            insert(i,a[x],1);
            insert(a[x],i,1);
        }
    }
    for(int i=1;i<=n;i++)
    {
        int x=read();
        if(a[x])
        {
            insert(i,a[x],0);
            insert(a[x],i,0);
        }else
            if(!b[x]) b[x]=i; else
            {
                insert(i,b[x],1);
                insert(b[x],i,1);
            }
    }
    for(int i=1;i<=n;i++)
        if(!bz[i])
        {
            sum=f[i]=0;
            dfs(i);
            int k=sum;
            sum=0,f[i]=1;
            dfs1(i);
            if(k<sum) sum=k;
            ans+=sum;
        }
    printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值