[HEOI2012]朋友圈
[HEOI2012]朋友圈
0x01 题意
有俩国家,国家里的每一个人有一个友善值,在A国,当且仅当两个人的友善值异或起来为奇数时两人为朋友,在B国,当且仅当两个人的友善值异或起来为偶数或着两个人的友善值或起来的二进制数有奇数个1时,两人为朋友,AB国之间有一些人是朋友,求该图的最大团。
0x02 解
建图时发现的性质:
众所周知,异或是不进位的加法。所以在A国,友善值是奇数的人和友善值是偶数的人是朋友,在B国,友善值同是奇数或同是偶数的人是朋友,以及两人友善值或起来的二进制数有奇数个1的人是朋友
如果要求最大团:
A国里最多选2个人,B国可以看作一个二分图,左部点为友善值是奇数的,右部点为友善值是偶数的,之间有一些边相连
可以暴力判A国人,B国用匈牙利算法解决,这里有一个优化叫时间戳优化
时间戳优化:
原来的匈牙利算法每次找增广路时都要把vis memset一下,是\(O(n)\)的复杂度,如果用一个整型数组来代替vis,每次改变一下基准值num,即
\[vis[v]=1 \rightarrow vis[v]=num
\]
\[vis[v]=0\rightarrow vis[v]\neq num
\]
实现\(O(1)\)初始化
0x03 码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#include<deque>
#include<set>
#include<stack>
#include<bitset>
#include<cstring>
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
using namespace std;
const int INF=0x3f3f3f3f,E=10000000,N=3310;
inline int read(){
int x=0,y=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') y=-1;c=getchar();}
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*y;
}
int e[E],ne[E],h[N],v[N],idx;
int t,n1,n2,m,a[N],b[N];
int mapp[3310][3310];
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int match[N],num,all,tot,ans,id;
int st[N],bl[N];
bool find(int x){
for(int i=h[x];~i;i=ne[i]){
int j=e[i];
if(st[j]!=num&&bl[j]==id){
st[j]=num;
if(match[j]==0||find(match[j])){
match[j]=x;
return true;
}
}
}
return false;
}
bool qs(int x){
int tot=0;
while(x){if(x&1) tot++;x>>=1;}
if(tot%2==1) return 1;
return 0;
}
int main(){
memset(h,-1,sizeof h);
t=read();
while(t--){
n1=read();n2=read();m=read();
for(int i=1;i<=n1;i++) a[i]=read();
for(int i=1;i<=n2;i++) b[i]=read();
for(int i=1;i<=n2;i++) if(b[i]&1)
for(int j=1;j<=n2;j++){
if(!(b[j]&1)&&(!qs(b[i]|b[j])))
add(i,j);
}
for(int i=1;i<=m;i++){
int x,y;
x=read(),y=read();
mapp[x][y+n1]=mapp[y+n1][x]=1;
}
for(int i=1;i<=n2;i++){
if(b[i]&1){num++;if(find(i)) ans++;}
}
ans=n2-ans;
for(int i=1;i<=n1;i++){
id++;
tot=0;all=0;
memset(match,0,sizeof match);
for(int j=1;j<=n2;j++)
if(mapp[i][j+n1]) bl[j]=id,all++;
for(int j=1;j<=n2;j++)
if(bl[j]==id&&(b[j]&1)){num++;if(find(j)) tot++;}
ans=max(ans,all-tot+1);
}
for(int i=1;i<=n1;i++){
for(int j=i+1;j<=n1;j++){
if((a[i]^a[j])&1){
memset(match,0,sizeof match);
id++;
tot=0;all=0;
for(int k=1;k<=n2;k++)
if(mapp[i][k+n1]&&mapp[j][k+n1]) bl[k]=id,all++;
for(int k=1;k<=n2;k++)
if(bl[k]==id&&(b[k]&1)){num++;if(find(k)) tot++;}
ans=max(ans,all-tot+2);
}
}
}
cout<<ans<<endl;
}
return 0;
}