题意:略
题解:一道将自己隐藏得很好的匹配问题,直接说建图方法吧(按照网络流的方式叙述的,想写km的可以自行转换)
对于N座城市,每座城市都看做一个点,并直接与源点相连,容量为1,对于操作1,将其看做一个点,将此时在联通
块中的点均与其相连,容量为1,然后再将其与汇点相连,容量为k(km的话,这里就需要加k个点了,所以跑得并没有
网络流快)对于2,3操作,就是就按照它所说删边加边就是了
因为要按字典序拍序,于是可以转成费用流,越靠后的点费用越小(解释得有点模糊,详见代码)
这里只有费用流代码,km又麻烦又慢,不想写,费用流虽然看着有点长,但大多都是版
code:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
const int inf=0x3f3f3f3f;
const int MAXN=2000+5;
const int MAXM=2000+5;
const int MAXK=2000+5;
using namespace std;
struct node{
int c,next,e,cost;
}h[MAXM*MAXN];
int fir[MAXN+MAXM],cnt=1,ans,N,K;
bool inq[MAXN+MAXM],vis[MAXN+MAXM];
int dis[MAXN],source,target,year;
queue<int> Q; int temp[MAXN],acnt;
bool Map[MAXN][MAXN],flag[MAXN];
inline void addedge(int t1,int t2,int c,int cost){
h[++cnt].e=t2;
h[cnt].next=fir[t1];
h[cnt].c=c; h[cnt].cost=cost;
fir[t1]=cnt;
h[++cnt].e=t1;
h[cnt].next=fir[t2];
h[cnt].cost=-cost;
fir[t2]=cnt;
}
void dfs(int s){
flag[s]=1; addedge(s+1,year+N+1,1,-year);
for(int i=1;i<=N;i++)
if(Map[s][i]&&!flag[i]) dfs(i);
}
inline bool spfa(){
Q.push(target);
memset(dis,0x3f,sizeof dis);
dis[target]=0; inq[target]=1;
while(!Q.empty())
{
int s=Q.front(); Q.pop();
inq[s]=0;
for(int i=fir[s];i;i=h[i].next)
{
int e=h[i].e;
if(h[i^1].c<=0) continue;
if(dis[e]>dis[s]+h[i^1].cost)
{
dis[e]=dis[s]+h[i^1].cost;
if(!inq[e])
Q.push(e),inq[e]=1;
}
}
}
if(dis[source]==dis[0]) return 0;
else return 1;
}
int aug(int s,int augco){
if(s==target)
{
ans+=dis[source]*augco;
return augco;
}
int delta,augc=augco;
for(int i=fir[s];i&&augc>0;i=h[i].next)
{
int e=h[i].e;
if(vis[e]||h[i].c<=0) continue;
if(dis[s]==dis[e]+h[i].cost)
{
vis[e]=1;
delta=min(h[i].c,augc);
delta=aug(e,delta);
h[i].c-=delta;
h[i^1].c+=delta;
augc-=delta;
}
}
return augco-augc;
}
int costflow(){
ans=0; int flow=0;
while(spfa())
{
memset(vis,0,sizeof vis);
vis[source]=1; flow+=aug(source,inf);
}
return flow;
}
inline void Clear(){
cnt=1; acnt=year=0;
memset(Map,0,sizeof Map);
memset(fir,0,sizeof fir);
memset(h,0,sizeof h);
memset(temp,0,sizeof temp);
}
int T,M,opt,x,y,q;
int main()
{
scanf("%d",&T);
while(T--)
{
Clear();
scanf("%d%d%d",&N,&M,&K);
for(int i=1;i<=M;i++)
{
scanf("%d",&opt);
switch(opt)
{
case 1:
scanf("%d",&x);
year++; dfs(x);
memset(flag,0,sizeof flag);
break;
case 2:
scanf("%d%d",&x,&y);
Map[x][y]=Map[y][x]=1;
break;
case 3:
scanf("%d",&q);
while(q--)
{
scanf("%d%d",&x,&y);
Map[x][y]=Map[y][x]=0;
}
break;
}
}
target=year+N+2; source=1;
for(int i=1;i<=year;i++)
addedge(i+N+1,target,K,0);
for(int i=1;i<=N;i++) addedge(1,i+1,1,0);
printf("%d\n",costflow());
for(int i=fir[target];i;i=h[i].next)
temp[++acnt]=h[i].c;
for(int i=acnt;i;i--)
if(i!=1) printf("%d ",temp[i]);
else printf("%d\n",temp[i]);
}
}
/*
n 1000
m 1000
k 1000
这道题呢,到是有点思路,最开始,先把输入离线下来
然后看,对于1操作,将k个城市拆成k个点,将处于该联通
块内的每个城市都与这k个点相连,权值为这座城市出现的次数
至于加边加点的操作,用矩阵应该比较方便吧,或者把所有
联通块都给存下来?
只是这个
建图方法:
将源点与所有N座城市均连一条边,容量为1,费用为0
对于每次1操作,将所有联通块内的节点与该年连边,容量为1,
费用为之前进行了几次1操作的数量取负,即越靠后的费用越小
最后,把所有年份与汇点连边,容量为K,费用为0
1
5 6 2
2 1 2
2 1 3
1 1
1 2
3 1 1 2
1 2
*/