題面見校內OJ4694
題解:
首先來看標程的不用費用流的沒什麼拓展性的做法。
考慮對於每一條狗,我們建立如下的七個點
然後對於每個能夠打到這條狗的炮臺,向這條狗的五個藍點全部連邊。
然後跑一般圖最大匹配,考慮這條狗被攻擊到若干次之後內部能夠形成的最大匹配。
顯然就是這個時候造成的傷害值就是內部能夠形成的最大匹配。
800個點跑一般圖最大匹配,隨便寫一個帶花樹或者Tutte矩陣就行了(由於圖十分稠密,帶花樹跑得很快)。
接下來讓我們忘記上面那個沒有拓展性的做法。
仔細考慮一下這個問題,顯然是一個衝突貪心,雖然貪心代價不太好算,我們還是考慮利用費用流。
每條狗建立這樣五個點:
然後源點向每個炮臺連容量爲1的邊,炮臺向能夠打到的狗的5號點連容量爲1的邊。
現在把每條狗的五個點連向T的邊按照邊權降序賦值,然後跑最小費用最大流。
接着把所有與T直接相連的邊掃一遍,算貢獻。顯然這樣每條狗流滿了的邊是一個前綴。
流了幾條邊就代表扣了幾點耐久度。
現在有一個問題,我們發現流滿兩個4不如流一個4之後再流一個三。
考慮到需要退流重流,發現是一個一般圖最大匹配。考場上寫了Tutte矩陣的做法就A了。
(費用流or直接建圖)+(帶花樹orTutte矩陣求秩)四種寫法我全部寫了一遍。
由於兩種方式建出來的圖都十分稠密,所以帶花樹求最大匹配收斂賊快,比Tutte矩陣快得多。
代碼(費用流+帶花樹):
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
using std::cerr;
using std::cout;
cs int N=1e4+7;
int n,m,S,T,tot;
struct edge{int to,cap,w,rev;};
std::vector<edge> G[N];
typedef std::vector<edge>::iterator iter;
iter cur[N];
inline void adde(int u,int v,int cap,int cost){
G[u].push_back((edge){v,cap,cost,(int)G[v].size()});
G[v].push_back((edge){u,0,-cost,(int)G[u].size()-1});
}
inline int find_T(int u){
for(iter e=G[u].begin();e!=G[u].end();++e)if(e->to==T)return e->cap;
return 0;
}
int tot_cost,tot_flow;
int dis[N],vis[N];
inline bool SPFA(){
std::queue<int> q;
memset(dis+1,0x3f,sizeof(int)*tot);dis[S]=0;q.push(S);
for(int re i=1;i<=tot;++i)cur[i]=G[i].begin();
while(!q.empty()){
int u=q.front();vis[u]=false;q.pop();
for(iter e=G[u].begin();e!=G[u].end();++e)
if(e->cap&&dis[e->to]>dis[u]+e->w){
dis[e->to]=dis[u]+e->w;
if(!vis[e->to])vis[e->to]=true,q.push(e->to);
}
}
return dis[T]<1e9;
}
int st[N],tp;
int dfs(int u,cs int flow){
if(u==T){
tot_cost+=flow*dis[T];
tot_flow+=flow;
return flow;
}
vis[u]=true;int ans=0;
for(iter &e=cur[u];e!=G[u].end();++e){
if(e->cap&&dis[e->to]==dis[u]+e->w&&!vis[e->to]){
int delta=dfs(e->to,std::min(flow-ans,e->cap));
if(delta){
e->cap-=delta;
G[e->to][e->rev].cap+=delta;
if((ans+=delta)==flow)break;
}
}
}
vis[u]=false;
return ans;
}
inline void Flow(){//最小費用最大流
tot_cost=tot_flow=0;
while(SPFA())dfs(S,0x3f3f3f3f);
}
std::vector<int> pre[N],bin[N];
namespace G_Match{
cs int N=1e2+1;
int m;
std::vector<int> G[N];
int con[N][N];
inline void adde(int u,int v){if(con[u][v])return ;
con[u][v]=con[v][u]=true;
G[u].push_back(v),G[v].push_back(u);
}
int match[N],q[N],hd,tl;
int fa[N],pre[N],tim,tic[N],typ[N];
inline int gf(int u){return u==fa[u]?u:(fa[u]=gf(fa[u]));}
inline int LCA(int u,int v){
for(++tim;;std::swap(u,v))if(u){
u=gf(u);
if(tic[u]==tim)return u;
else tic[u]=tim,u=pre[match[u]];
}
}
inline void shrink(int u,int v,int p){
while(gf(u)!=p){
pre[u]=v,v=match[u];
if(typ[v]==2)typ[v]=1,q[++tl]=v;
if(gf(u)==u)fa[u]=p;
if(gf(v)==v)fa[v]=p;
u=pre[v];
}
}
inline int aug(int s){
for(int re i=1;i<=m;++i)fa[i]=i;
memset(typ,0,sizeof(int)*(m+1));
memset(pre,0,sizeof(int)*(m+1));
typ[q[hd=tl=1]=s]=1;
while(hd<=tl){
int u=q[hd++];
for(int re v:G[u]){
if(gf(v)==gf(u)||typ[v]==2)continue;
else if(!typ[v]){
typ[v]=2,pre[v]=u;
if(!match[v]){
for(int re t=v,las,tp;t;t=las){
las=match[tp=pre[t]];
match[t]=tp,match[tp]=t;
}
return true;
}
typ[match[v]]=1,q[++tl]=match[v];
}
else if(typ[v]==1){
int p=LCA(u,v);
shrink(u,v,p);
shrink(v,u,p);
}
}
}
return false;
}
inline int calc(){
int ans=0;
for(int re i=1;i<=m;++i)ans+=(!match[i]&&aug(i));
return ans;
}
inline void clear(){
for(int re i=1;i<=m;++i)G[i].clear(),memset(con[i],0,sizeof(int)*(m+1));
memset(match,0,sizeof(int)*(m+1));
}
inline int solve(){
int siz=0;
for(int re i=1;i<=n;++i)siz=std::max(siz,(int)bin[i].size());
if(siz<=1)return 0;
clear();
for(int re i=1;i<=n;++i){
for(int re j=0;j<bin[i].size();++j)
for(int re k=j+1;k<bin[i].size();++k)adde(bin[i][j],bin[i][k]);
}
return calc();
}
}
int idc[N],idd[N][6];
inline void solve(){
scanf("%d%d",&n,&m);tot=0;
for(int re i=1;i<=n;++i)idc[i]=++tot,bin[i].clear();
for(int re i=1;i<=m;++i)
for(int re j=1;j<=5;++j)idd[i][j]=++tot;
S=++tot,T=++tot;
for(int re i=1;i<=tot;++i)G[i].clear();
for(int re i=1;i<=n;++i)adde(S,idc[i],1,0);
for(int re i=1;i<=m;++i){
pre[i].clear();
adde(idd[i][5],idd[i][4],4,0);
adde(idd[i][4],idd[i][3],3,0);
adde(idd[i][3],idd[i][2],2,0);
adde(idd[i][2],idd[i][1],1,0);
adde(idd[i][5],T,1,-105);
adde(idd[i][4],T,1,-99);
adde(idd[i][3],T,1,-99);
adde(idd[i][2],T,1,-89);
adde(idd[i][1],T,1,-84);
}
for(int i=1;i<=n;++i){
int k,t;scanf("%d",&k);std::vector<int> v;
while(k--){scanf("%d",&t);v.push_back(t);pre[t].push_back(i);}
std::sort(v.begin(),v.end());
for(int re j=0;j<(int)v.size();++j)adde(idc[i],idd[v[j]][5],1,0);
}
Flow();assert(tot_flow==n);
int res=m*3;
for(iter e=G[T].begin();e!=G[T].end();++e){
if(e->cap&&(e->w==84||e->w==89||e->w==99)){
--res;
}
}int &cnt=G_Match::m;cnt=0;
for(int i=1;i<=m;++i){
int t1=find_T(idd[i][4]),t2=find_T(idd[i][3]);
if(!t1&&!t2)++res;
if(t1^t2){++cnt;
for(int re j=0;j<(int)pre[i].size();++j)bin[pre[i][j]].push_back(cnt);
}
}
res+=G_Match::solve();
std::cout<<res<<"\n";
}
signed main(){
#ifdef zxyoi
freopen("fortress.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
freopen("fortress.in","r",stdin);freopen("fortress.out","w",stdout);
#endif
#endif
int T;scanf("%d",&T);srand(time(0));
while(T--)solve();
return 0;
}
代碼(直接建圖跑一般圖最大匹配,我寫的Tutte矩陣求秩,慢得嚇人):
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
using std::cerr;
using std::cout;
namespace G_Match{
cs int N=8e2+7;
int a[N][N],b[N][N],m;
cs int mod=1e9+7;
inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
inline int dec(int a,int b){a-=b;return a+(a>>31&mod);}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b,int res=1){
for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
return res;
}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}
inline int calc(){//rank
for(int re i=1;i<=m;++i)
for(int re j=1;j<=m;++j){
if(i<j){
if(a[i][j])b[i][j]=mul(abs(rand()),abs(rand()));
else b[i][j]=0;
}
else if(i==j)b[i][j]=0;
else b[i][j]=dec(0,b[j][i]);
}
int ans=0;
for(int re i=1;i<=m;++i){
int p;for(p=i;p<=m;++p)if(b[p][i])break;
if(p>m)continue;++ans;
if(i!=p)for(int re j=i;j<=m;++j)std::swap(b[i][j],b[p][j]);
int inv=power(b[i][i],mod-2);
for(int re j=i;j<=m;++j)Mul(b[i][j],inv);
for(int re j=i+1,t;j<=m;++j)if(!!(t=b[j][i]))
for(int re k=i;k<=m;++k)Dec(b[j][k],mul(t,b[i][k]));
}
return ans>>1;
}
inline void adde(int u,int v){a[u][v]=a[v][u]=true;}
inline void clear(){
memset(a,0,sizeof a);
}
inline int solve(){return std::min(calc(),calc());}
}
using G_Match::adde;
cs int N=1e2+7;
int &tot=G_Match::m;
int n,m;
int idc[N],idd[N][10];
inline void solve(){
scanf("%d%d",&n,&m),tot=0;
for(int re i=1;i<=n;++i)idc[i]=++tot;
for(int re i=1;i<=m;++i)
for(int re j=0;j<7;++j)idd[i][j]=++tot;
G_Match::clear();
for(int re i=1;i<=m;++i){
adde(idd[i][0],idd[i][1]);
adde(idd[i][1],idd[i][2]);
adde(idd[i][2],idd[i][3]);
adde(idd[i][3],idd[i][4]);
adde(idd[i][4],idd[i][0]);
adde(idd[i][5],idd[i][0]);
adde(idd[i][5],idd[i][1]);
adde(idd[i][5],idd[i][2]);
adde(idd[i][5],idd[i][3]);
adde(idd[i][5],idd[i][4]);
adde(idd[i][6],idd[i][0]);
adde(idd[i][6],idd[i][1]);
adde(idd[i][6],idd[i][2]);
adde(idd[i][6],idd[i][3]);
adde(idd[i][6],idd[i][4]);
}
for(int re i=1;i<=n;++i){
int k,v;scanf("%d",&k);
while(k--){
scanf("%d",&v);
adde(idc[i],idd[v][0]);
adde(idc[i],idd[v][1]);
adde(idc[i],idd[v][2]);
adde(idc[i],idd[v][3]);
adde(idc[i],idd[v][4]);
}
}
std::cout<<G_Match::calc()-n<<"\n";
}
signed main(){
#ifdef zxyoi
freopen("fortress.in","r",stdin);
#endif
int T;scanf("%d",&T);srand(time(0));
while(T--)solve();
return 0;
}