[bzoj4874]筐子放球

来自FallDream的博客,未经允许,请勿转载,谢谢。

小N最近在研究NP完全问题,小O看小N研究得热火朝天,便给他出了一道这样的题目:
有 n 个球,用整数 1 到 n 编号。还有 m 个筐子,用整数1到m编号。
每个球只能放进特定的两个筐子之一,第 i 个球可以放进的筐子记为 Ai 和 Bi 。
每个球都必须放进一个筐子中。
如果一个筐子内有奇数个球,那么我们称这样的筐子为半空的。
求半空的筐子最少有多少个。
小N看到题目后瞬间没了思路,站在旁边看热闹的小I嘿嘿一笑:"水题!"
然后三言两语道出了一个多项式算法。
小N瞬间就惊呆了,三秒钟后他回过神来一拍桌子:
"不对!这个问题显然是NP完全问题,你算法肯定有错!"
小I浅笑:"所以,等我领图灵奖吧!"
小O只会出题不会做题,所以找到了你--请你对这个问题进行探究,并写一个程序解决此题
 
n,m<=200000
感觉是一道智商题 所以来做了做
想了半天只会一个log的做法
考虑线性基 每次有一个球可以选择a,b(a<b),就先把a的权值塔上1,然后加入只有a和b位是1的线性基
直接暴力插入是n^2,但是发现一个点出发的很多条边,在选择了一个基之后,假设基是u->v,那么u->其他点的边都变成了v->其他点的边,所以可并堆维护即可,复杂度nlogn
当然只有这道题才能满足线性基贪心
 
然后去搜了搜题解,发现答案就是有奇数条边的联通块个数 
因为偶数条边一定能满足两两配对 应该是可以证明的吧
所以直接dfs就行了 复杂度O(n)
线性基+可并堆
#include<iostream>
#include<cstdio>
#include<queue>
#define INF 2000000000
#define Dis(x) (x?x->dis:0)
#define MN 200000
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

int n,m,s[MN+5],to[MN+5],ans=0;
struct Tree
{
    Tree *l,*r;int dis,x;    
     Tree(int k){dis=0;x=k;l=r=NULL;}
     int top(){return x;}
     friend Tree* Merge(Tree*a,Tree*b)
     {
         if(!a) return b;
         if(!b) return a;
         if(a->x>b->x) swap(a,b);
         a->r=Merge(a->r,b);
         if(Dis(a->l)<Dis(a->r)) swap(a->l,a->r);
         a->dis=Dis(a->r)+1;
         return a;
     }
     Tree* pop(){return Merge(l,r);}
     Tree* ins(int x){return Merge(new Tree(x),this);}
}*rt[MN+5];

int main()
{
    m=read();n=read();
    for(int i=1;i<=n;i++) s[i]=1,rt[i]=new Tree(INF);
    for(int i=1;i<=m;++i)
    {
        int x=read(),y=read();
        if(x>y) swap(x,y);
        s[x]^=1;rt[x]=rt[x]->ins(y);
    }     
    for(int i=1;i<=n;++i)
    {    
        while(rt[i]->top()==i) rt[i]=rt[i]->pop();
        int x=rt[i]->top();
        if(x==INF) continue;
        to[i]=x; 
        rt[x]=Merge(rt[x],rt[i]);
    }
    for(int i=1;i<=n;++i)
    {
        if(to[i]&&!s[i]) s[i]^=1,s[to[i]]^=1;
        ans+=s[i]; 
    }
    cout<<n-ans<<endl;
    return 0;
}

靠谱做法

#include<iostream>
#include<cstdio>
#define getchar() (*S++)
#define MN 200000
char B[1<<26],*S=B;
using namespace std;
inline int read()
{
    int x = 0; char ch = getchar();
    while(ch < '0' || ch > '9')  ch = getchar();
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x;
}

int head[MN+5],ans=0,sum=0,cnt=0,n,m;
struct edge{int to,next,w;}e[MN*2+5];
bool mark[MN+5];

inline void ins(int f,int t)
{
    e[++cnt]=(edge){t,head[f]};head[f]=cnt;
    e[++cnt]=(edge){f,head[t]};head[t]=cnt;    
}

void dfs(int x)
{
    mark[x]=1;
    for(int i=head[x];i;i=e[i].next,++sum)
        if(!mark[e[i].to]) dfs(e[i].to);    
}

int main()
{
    fread(B,1,1<<26,stdin);
    m=read();n=read();
    for(register int i=1;i<=m;++i) ins(read(),read());
    for(register int i=1;i<=n;++i)if(!mark[i])
    {
        sum=0;dfs(i);
        ans+=((sum/2)&1);
    }
    cout<<ans;
    return 0;
}
posted @ 2017-05-01 12:11  FallDream  阅读(405)  评论(0编辑  收藏  举报