题意:MZL是个女孩,他有一个自己的国家。
她的国家有N个城市,标号从1到N,她已经控制这个国家很长时间了并且她记得M年前发生了一次大地震,使得所有城市之间的道路都被破坏了,并且所有的城市也被摧毁。她还记得在之后M年的每一年发生的一件事情。
这些事情可能是以下三种之一(也就是一年只发生其中之一事件):
1.她重建了与X城市直接或者间接相连并且包括X城市的城市群。(注意:如果一个城市被重建那么这个城市就不会被再次损毁)
2.城市X和城市Y之间连接的是双向边
3.在这M年间的某年,发生了一次地震,地震摧毁了一些路
MZL忘记了确切重建了哪些城市。但是MZL知道每年重建的城市个数不能超过K个。现在她想要知道这个M年间最多可以重建多少个城市。与此同时,她还想知道,在保证最多重建城市个数的前提下,每一次重建城市的个数是多少,并且按照字典序最小输出。比如说:8年间,其中有3年重建了一些城市,加入最多可以建造11个城市,那么有可能分配方案是:重建的第一个年头8,第二个年头2,第三个年头1,这三个年头可以不连续。那么也有可能的分配方案是:第一个年头10,第二个年头0, 第三个年头1,但是前一个方案是满足提议的因为 字典序(8, 2, 1) < 字典序(10, 0, 1)。
以上是提议部分,输入是:
先输入T,表示输入的案例个数。
然后输入N,M,K分别表示,城市的数量,年数,每一次重建的城市,最多建造K个。接下来是M行,每行分别是1,2,3操作(上面已经解释过了)。对于2操作,紧接着u,v表示重建此路,对于3操作紧接着num,然后有num条路被摧毁,然后是num个u,v。
想法:可以想到最大流,因为有K的限制,对于每年需要建造的操作可以进行很多城市的选择,但是管道的流量是上限K,这是就可以进行一个大略的建图:
1.设置source和sink,源点和汇点。
2.每一个城市向sink连接一条容量为1的边。代表是否建造这个城市。
3.sink向每一次“建设年”的操作连接一条容量为K的边,表示今年可以建造最多K个城市,也就是0~K个城市都行。然后如果从“建设年” 向 建设年建造的城市以及其可以到达的城市构成的集合中的每一个元素城市连接一条容量为1的边,表示是否建造这个城市,因为容量只能是0/1,这就很显然形成了对可以建造城市的数量的选择。
那么接下来的问题就是考虑最小的字典序问题。显然就是能前几个年头能不建城市就不建城市,能少建就少建城市,留着之后建设。也就是说尽可能的让最近“建设年”的城市多重建,也就是寻找增广路的时候希望最近的“建设年”城市先被增广。那么此时就非常容易引入最短路,也就是说增广时候按照最短路去增广,给每一条边加入一个费用,越近的“建设年”费用就低,这样就完美解决了最小子序列问题。也就是对于建图的第2条,给每条边一个费用0,或者统一都是一个量就行,然后给第3条,按照“建造年”的时间顺序给一个费用,时间越近费用越低,所以最简单的就是给一个初值fee = 1000(因为1000>M)然后每到一个“建设年”,fee减去1,这样构成递减,至于减多少,自己定,只要够减500次就行,每次减少至少为1。然后撸一遍最小费用最大流完事,输出的是最大流。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<cstdlib>
#define INF 0x7fffffff
using namespace std;
const int max_node_size = 210;
const int max_op_size = 510;
int N, M, K;
int T;
bool mark_graph[max_node_size][max_node_size], visit[max_node_size];
int ans[max_op_size];
class static_AdjList
{
public:
struct Edge
{
int u;
int v;
int flow;
int fee;
int next;
};
struct Edge *edge;
int *head;
int cnt;
~static_AdjList(){}
void _init(int num_node, int num_edge);
void AddEdge(int u, int v, int flow, int fee);
};
void static_AdjList::_init(int num_node, int num_edge)
{
struct Edge *tmp_edge = new Edge[num_edge];
edge = tmp_edge;
tmp_edge = NULL;
delete[] tmp_edge;
int *tmp_head = new int[num_node];
head = tmp_head;
tmp_head = NULL;
delete[] tmp_head;
cnt = 0;
for(int i = 0; i < num_node; i++)
{
head[i] = -1;
}
}
void static_AdjList::AddEdge(int u, int v, int flow, int fee)
{
edge[cnt].u = u;
edge[cnt].v = v;
edge[cnt].flow = flow;
edge[cnt].fee = fee;
edge[cnt].next = head[u];
head[u] = cnt++;
edge[cnt].u = v;
edge[cnt].v = u;
edge[cnt].flow = 0;
edge[cnt].fee = -fee;
edge[cnt].next = head[v];
head[v] = cnt++;
}
class MCMF
{
public:
static_AdjList sadjlist;
~MCMF(){}
void _init(int num_node, int num_edge, int s, int t);
bool SPFA();
int Get_MCMF();
private:
int *depth;
int *search_back;
int *mark_edgeid;
int min_cost;
int source, sink;
int n_node, n_edge;
};
void MCMF::_init(int num_node, int num_edge, int s, int t)
{
int *tmp_depth = new int[num_node];
depth = tmp_depth;
tmp_depth = NULL;
delete[] tmp_depth;
int *tmp_search_back = new int[num_node];
search_back = tmp_search_back;
tmp_search_back = NULL;
delete[] tmp_search_back;
int *tmp_mark_edgeid = new int[num_node];
mark_edgeid = tmp_mark_edgeid;
tmp_mark_edgeid = NULL;
delete[] tmp_mark_edgeid;
sadjlist._init(num_node, num_edge);
min_cost = 0;
source = s;
sink = t;
n_node = num_node;
n_edge = num_edge;
}
bool MCMF::SPFA()
{
bool *visited_node = new bool[n_node];
queue<int>q;
while(!q.empty()) q.pop();
for(int i = 0; i < n_node; i++)
{
depth[i] = INF;
search_back[i] = -1;
visited_node[i] = false;
}
depth[source] = 0;
visited_node[source] = true;
q.push(source);
while(!q.empty())
{
int cur_node = q.front();
q.pop();
visited_node[cur_node] = false;
for(int i = sadjlist.head[cur_node]; i + 1; i = sadjlist.edge[i].next)
{
int next_node = sadjlist.edge[i].v;
if(depth[next_node] > depth[cur_node] + sadjlist.edge[i].fee && sadjlist.edge[i].flow > 0)
{
depth[next_node] = depth[cur_node] + sadjlist.edge[i].fee;
search_back[next_node] = cur_node;
mark_edgeid[next_node] = i;
if(!visited_node[next_node])
{
visited_node[next_node] = true;
q.push(next_node);
}
}
}
}
delete[] visited_node;
if(depth[sink] != INF) return true;
else return false;
}
int MCMF::Get_MCMF()
{
int ans_mcmf = 0;
while(SPFA())
{
int flow = INF;
for(int cur_node = sink; cur_node != source; cur_node = search_back[cur_node])
{
flow = min(flow, sadjlist.edge[mark_edgeid[cur_node]].flow);
}
for(int cur_node = sink; cur_node != source; cur_node = search_back[cur_node])
{
int edgeid = mark_edgeid[cur_node];
sadjlist.edge[edgeid].flow -= flow;
sadjlist.edge[edgeid ^ 1].flow += flow;
}
ans_mcmf += flow;
}
return ans_mcmf;
}
void dfs(int city, int op_index, MCMF &mcmf)
{
mcmf.sadjlist.AddEdge(op_index, city, 1, 0);
visit[city] = true;
for(int i = 1; i <= N; i++)
{
if(!visit[i] && mark_graph[city][i])
{
dfs(i, op_index, mcmf);
}
}
}
int main()
{
scanf("%d", &T);
while(T--)
{
MCMF mcmf;
scanf("%d%d%d", &N, &M, &K);
int st = 0;
int ed = N + 1;
int max_fee = 551;
int index = ed;
int cnt_temp = 0;
mcmf._init(N + M + 2, (M * N + N + M) * 2, st, ed);
for(int i = 1; i <= N; i++)
{
mcmf.sadjlist.AddEdge(i, ed, 1, 0);
}
memset(mark_graph, false, sizeof(mark_graph));
for(int i = 1; i <= M; i++)
{
int op;
scanf("%d", &op);
switch(op)
{
case 1:
int city;
scanf("%d", &city);
memset(visit, false, sizeof(visit));
dfs(city, ++index, mcmf);
ans[cnt_temp++] = mcmf.sadjlist.cnt;
mcmf.sadjlist.AddEdge(st, index, K, --max_fee);
break;
case 2:
int u, v;
scanf("%d%d", &u, &v);
mark_graph[u][v] = true;
mark_graph[v][u] = true;
break;
case 3:
int num;
scanf("%d", &num);
while(num--)
{
int u, v;
scanf("%d%d", &u, &v);
mark_graph[u][v] = false;
mark_graph[v][u] = false;
}
break;
default:
break;
}
}
if(!cnt_temp)
{
printf("0\n");
continue;
}
printf("%d\n", mcmf.Get_MCMF());
for(int i = 0; i < cnt_temp; i++)
{
if(i) printf(" ");
printf("%d", mcmf.sadjlist.edge[ans[i] ^ 1].flow);
}
printf("\n");
}
return 0;
}