传送门1
传送门2
思路:
思维不错的一道题。
题意就是给出限制条件,在构建出的图上找最大团,如果是一般图找最大团是NPC问题,所以要转换思路,一看数据范围很像网络流,但是一开始我并没有想到怎么做,也并没有什么比较好的建图方式。
最初的想法就是A国只能选两个(显然),关键在于B国怎么选,B国朋友的限制条件是”或”,所以并不能直接建二分图,绞尽脑汁想了很久也没有什么办法,偷瞄了一眼题解(啪),看到有一句话说要考虑逆问题,直接ctrl+w后继续想,发现如果把B国朋友的限制条件反过来,那么这正是二分图的建图条件,感觉自己还是too young。
把这个瓶颈突破以后,之后的工作就比较容易了,枚举A国0个,1个,2个,然后对B国中符合条件的人分奇偶,建二分图并连边,跑匈牙利或dinic就可以了。
吐槽一下:这个题的数据太水了,我一开始写了一个不清空vis数组的匈牙利得了90分,还发现WA的10分并不是匈牙利的事情,而是我没判断选A国两个人时它们的xor值是否是奇数!判断以后就A了,唉
回到正题,如果每次暴力记录vis的时间戳以及当前点可不可行的话,跑匈牙利是跑不过的(好像只能有70分),学习了一下优化技巧,每次存下来合法的点,然后把图重构一下,这样就可以过剩下的3个点了
设点数为n,边数为m,匈牙利的时间复杂度最坏是
O(nm)
,dinic的单位流容量复杂度是
O(mn√)
?我的大部分题写的都是dinic,好像只有一两道没法用dinic,只能套匈牙利
提醒一下,并没有什么多组数据,BZOJ和codevs上甚至连数据组数都不用输入
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
#define inf 1e9
using namespace std;
int T,A,B,M;
int a[205],b[3005],X[3005],Y[3005];
bool ok[205][3005],not_friend[3005][3005];
namespace network
{
int s,t,tot,first[3005],dis[3005];
queue<int>q;
struct edge{
int v,w,next;
}e[6000005];
void init()
{
memset(first,0,sizeof(first));
tot=1;s=0;t=B+1;
}
void add(int x,int y,int z)
{
e[++tot].v=y;e[tot].w=z;e[tot].next=first[x];first[x]=tot;
e[++tot].v=x;e[tot].w=0;e[tot].next=first[y];first[y]=tot;
}
bool bfs()
{
memset(dis,0,sizeof(dis));
int x;
dis[s]=1;
for (q.push(s);!q.empty();q.pop())
{
x=q.front();
for (int i=first[x];i;i=e[i].next)
if (!dis[e[i].v]&&e[i].w)
dis[e[i].v]=dis[x]+1,
q.push(e[i].v);
}
return dis[t]>0;
}
int dfs(int x,int maxn)
{
if (x==t) return maxn;
int used=0,k;
for (int i=first[x];i;i=e[i].next)
if (dis[e[i].v]==dis[x]+1)
{
k=dfs(e[i].v,min(maxn-used,e[i].w));
e[i].w-=k;e[i^1].w+=k;
used+=k;
if (used==maxn) return maxn;
}
if (!used) dis[x]=0;
return used;
}
}
main()
{
scanf("%d%d%d",&A,&B,&M);
for (int i=1;i<=A;++i) scanf("%d",a+i);
for (int i=1;i<=B;++i) scanf("%d",b+i);
for (int u,v,i=1;i<=M;++i)
scanf("%d%d",&u,&v),
ok[u][v]=1;
network::init();
int tmp,num;
for (int i=1;i<=B;++i)
{
for (int j=1;j<i;++j)
if ((b[i]^b[j])&1)
{
tmp=(b[i]|b[j]);
num=0;
for (;tmp;tmp^=tmp&-tmp) ++num;
if (num&1);
else not_friend[i][j]=not_friend[j][i]=1;
}
if (b[i]&1) X[++X[0]]=i,network::add(network::s,i,1);
else Y[++Y[0]]=i,network::add(i,network::t,1);
}
for (int i=1;i<=X[0];++i)
for (int j=1;j<=Y[0];++j)
if (not_friend[X[i]][Y[j]])
network::add(X[i],Y[j],1);
int ans=B;
while (network::bfs()) ans-=network::dfs(network::s,inf);
int sum;
for (int i=1;i<=A;++i)
{
X[0]=Y[0]=0;
network::init();
for (int j=1;j<=B;++j)
{
if (ok[i][j])
if (b[j]&1) X[++X[0]]=j,network::add(network::s,j,1);
else Y[++Y[0]]=j,network::add(j,network::t,1);
}
sum=X[0]+Y[0]+1;
for (int j=1;j<=X[0];++j)
for (int k=1;k<=Y[0];++k)
if (not_friend[X[j]][Y[k]]) network::add(X[j],Y[k],1);
while (network::bfs()) sum-=network::dfs(network::s,inf);
ans=max(sum,ans);
}
for (int i=1;i<=A;++i)
for (int j=i+1;j<=A;++j)
if ((a[i]^a[j])&1)
{
X[0]=Y[0]=0;
network::init();
for (int k=1;k<=B;++k)
{
if (ok[i][k]&&ok[j][k])
if (b[k]&1) X[++X[0]]=k,network::add(network::s,k,1);
else Y[++Y[0]]=k,network::add(k,network::t,1);
}
sum=X[0]+Y[0]+2;
for (int k=1;k<=X[0];++k)
for (int l=1;l<=Y[0];++l)
if (not_friend[X[k]][Y[l]]) network::add(X[k],Y[l],1);
while (network::bfs()) sum-=network::dfs(network::s,inf);
ans=max(sum,ans);
}
printf("%d\n",ans);
}