BZOJ2744 [HEOI2012]朋友圈

传送门

题解:
显然A国模2的余数相同的没有边,即A国的朋友关系是二分图。那么A国的朋友圈最多2个人。(否则如果有3个人,就有2个人模2的余数相同,做不了朋友)。
B国稍微复杂,因为它本身并不是二分图。但它的补图是二分图,所以可以在补图上跑一发最大独立集,求法为总点数-最大匹配数。
然后枚举A中的点,对于B中和枚举的点都连接的点跑二分图匹配即可。
然后这道题需要注意讨论版:

leoly:对于B类点:偶数点连向奇数点,时间20000+ms;奇数点连向偶数点,时间2000+ms
kac:注意这题实际数据中 只有一组数据 而且没有输入t

话说我一部分memset一部分时间戳到底是要干什么,反正364ms过了

代码:

#include<cstdio>
#include<cstring>
#define maxb 3005
#define maxa 205
#define maxm 9000005
#define max(a,b) ((a)>(b)?(a):(b))
int na,nb,m,mx[maxb],my[maxb],a[maxa],b[maxb],cnt,ok[maxb],vis[maxb],ans,mat;
bool G[maxa][maxb];
struct node { int v; node *nxt; } edge[maxm],*head[maxb],*ncnt;
void addedge(int u,int v)
{
    ncnt++;
    ncnt->v=v,ncnt->nxt=head[u];
    head[u]=ncnt;
}
int dfs(int u)
{
    for(node *p=head[u];p;p=p->nxt)
    {
        int v=p->v;
        if(vis[v]==cnt||ok[v]!=cnt) continue;
        vis[v]=cnt;
        if(!my[v]||dfs(my[v])) { mx[u]=v,my[v]=u; return 1; }
    }
    return 0;
}
int Get(int x)
{
    int cnt=0;
    for(;x;x-=x&(-x)) cnt++;
    return cnt;
}
int main()
{
    scanf("%d%d%d",&na,&nb,&m);
    ncnt=&edge[0];
    for(int i=1;i<=na;i++) scanf("%d",&a[i]);
    for(int i=1;i<=nb;i++) scanf("%d",&b[i]);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        G[u][v]=true;
    }
    for(int i=1;i<=nb;i++)
        for(int j=i+1;j<=nb;j++)
            if((b[i]+b[j])%2&&Get(b[i]|b[j])%2==0)
            {
                if(b[i]%2) addedge(i,j);
                else addedge(j,i);
            }
    cnt++;
    for(int i=1;i<=nb;i++) ok[i]=cnt;
    for(int i=1;i<=nb;i++)
        if(!mx[i]&&b[i]%2) mat+=dfs(i);
    ans=nb-mat;
    for(int i=1;i<=na;i++)
    {
        memset(mx,0,sizeof(mx));
        memset(my,0,sizeof(my));
        cnt++,mat=0;
        int cb=0;
        for(int j=1;j<=nb;j++) 
            if(G[i][j]) ok[j]=cnt,cb++;
        for(int j=1;j<=nb;j++)
            if(ok[j]==cnt&&!mx[j]&&b[j]%2) mat+=dfs(j);
        ans=max(ans,1+cb-mat);
        for(int j=i+1;j<=na;j++)
            if((a[i]+a[j])%2)
            {
                cnt++,mat=cb=0;
                memset(mx,0,sizeof(mx));
                memset(my,0,sizeof(my));
                for(int k=1;k<=nb;k++)
                    if(G[i][k]&&G[j][k]) ok[k]=cnt,cb++;
                for(int k=1;k<=nb;k++)
                    if(ok[k]==cnt&&!mx[k]&&b[k]%2) mat+=dfs(k);
                ans=max(ans,2+cb-mat);
            }
    }
    printf("%d\n",ans);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值