Monito / 管道监控
题目链接:jzoj 7185
题目大意
有一个图,路径有一个字母。
然后你有一些操作,每个操作有费用和一串字符串。
然后你可以把它从一个点开始,按着字符串的字符一个一个走路径。
(当然要能走才可以放)
然后问你要让所有的边都被走过至少一次最小的费用。
思路
这道题正解是 DP。
但是,我们看看这道题。
我们发现这题似乎可以用网络流来做?
对于每个操作字符串我们把它自己 KMP 一下,然后丢进去原图跑,找到原图所有它的字符串,然后想这道题那样连边。
然后由于所有点需求都是一,我们这样搞,如果一个点入读比初度大
x
x
x,那就留
x
x
x 到终点,如果少
x
x
x,就起点留
x
x
x 到它。
然后跑网络流,最后的答案就有了。
代码
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
struct node {
char x;
int to, nxt;
}e[501];
struct rd {
ll x, to, nxt, op, val;
}e_[601 * 601];
int n, m, t, x, stn, le[501], KK, nxt[1000001], find_num;
int fa[501][501], deg[601], asktot, use[601][601], re, up[601];
int le_[601], KK_, S, T, chu[501], bll[601 * 601], le_e[601];
ll dis[601], ask[601 * 601][4], ans;
queue <int> q;
char c, s[1000001];
bool in[1001], cs[501];
int read() {
re = 0;
c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') {
re = (re << 3) + (re << 1) + c - '0';
c = getchar();
}
return re;
}
void addl(int x, int y, char c) {
e[++KK] = (node){c, y, le[x]}; le[x] = KK;
}
void add(int x, int y, ll z, ll val, int bh) {
if (use[x][y] && e_[use[x][y]].val <= val) return ;
if (!use[x][y]) {//丢进去匹配
e_[++KK_] = (rd){z, y, le_[x], KK_ + 1, val}; le_[x] = KK_;
e_[++KK_] = (rd){0, x, le_[y], KK_ - 1, -val}; le_[y] = KK_;
if (bh) {
asktot++;
bll[KK_ - 1] = asktot;
ask[asktot][0] = KK_ - 1;
ask[asktot][1] = x;
ask[asktot][2] = z;
ask[asktot][3] = bh;
}
use[x][y] = KK_ - 1;
}
else {//同一个区间可能会多次碰到,那我们只要再有更优的时候更新 val 就可以了
e_[use[x][y]].val = val;
e_[e_[use[x][y]].op].val = -val;
if (bll[use[x][y]]) ask[bll[use[x][y]]][3] = bh;
}
}
void dfs(int now, int j, int bh) {
if (j == stn) {//匹配到了
if (!use[now][fa[now][j - 1]])
up[now] = max(up[now], j);
add(now, fa[now][j - 1], INF, x, bh);
}
int tmpj = j;
for (int i = le[now]; i; i = e[i].nxt) {
while (j && e[i].x != s[j + 1]) j = nxt[j];
if (e[i].x == s[j + 1]) j++;
dfs(e[i].to, j, bh);
j = tmpj;
}
}
//网络流
bool SPFA() {
for (int i = 0; i <= T; i++) dis[i] = 1e18;
memset(deg, 0, sizeof(deg));
for (int i = 1; i <= T; i++) le_e[i] = le_[i];
deg[S] = 1;
dis[S] = 0;
in[S] = 1;
q.push(S);
while (!q.empty()) {
int now = q.front();
q.pop();
for (int i = le_[now]; i; i = e_[i].nxt)
if (dis[e_[i].to] > dis[now] + e_[i].val && e_[i].x) {
dis[e_[i].to] = dis[now] + e_[i].val;
deg[e_[i].to] = deg[now] + 1;
if (!in[e_[i].to]) {
in[e_[i].to] = 1;
q.push(e_[i].to);
}
}
in[now] = 0;
}
return dis[T] != dis[0];
}
ll Dfs(int now, ll sum) {
if (now == T) return sum;
ll go = 0;
in[now] = 1;
for (int &i = le_e[now]; i; i = e_[i].nxt)
if (e_[i].x && dis[e_[i].to] == dis[now] + e_[i].val && deg[e_[i].to] == deg[now] + 1 && !in[e_[i].to]) {
ll this_go = Dfs(e_[i].to, min(sum - go, e_[i].x));
if (this_go) {
e_[i].x -= this_go;
e_[e_[i].op].x += this_go;
go += this_go;
if (go == sum) {
in[now] = 0;
return go;
}
}
}
if (go != sum) dis[now] = -1;
in[now] = 0;
return go;
}
void dinic() {
while (SPFA())
ans += dis[T] * Dfs(S, INF);
}
int main() {
// freopen("read.txt", "r", stdin);
n = read(); m = read(); t = read();
S = n + 1;
T = n + 2;
for (int i = 2; i <= n; i++) {
x = read();
c = getchar();
while (c < 'a' || c > 'z') c = getchar();
addl(x, i, c);
fa[i][0] = x;
add(x, i, INF, 0, 0);
chu[x]++;
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
fa[i][j] = fa[fa[i][j - 1]][0];
for (int i = 1; i <= m; i++) {
x = read();
scanf("%s", s + 1);
stn = strlen(s + 1);
int j = 0;
for (int i = 2; i <= stn; i++) {//跑 KMP
while (j != 0 && s[i] != s[j + 1]) j = nxt[j];
if (s[i] == s[j + 1]) j++;
nxt[i] = j;
}
dfs(1, 0, i);
}
add(1, T, INF, 0, 0);
for (int i = 1; i <= n; i++)
if (!chu[i]) add(S, i, 1, 0, 0);
else if (chu[i] > 1) add(i, T, chu[i] - 1, 0, 0);
dinic();
for (int i = 1; i <= n; i++) {
int tmp = i;
for (int j = 1; j <= up[i]; j++) {
if (!cs[tmp]) {
cs[tmp] = 1;
find_num++;
}
tmp = fa[tmp][0];
}
}
if (find_num != n - 1) printf("-1");
else {
printf("%lld\n", ans);
if (t == 1) {
int num = 0;
for (int i = 1; i <= asktot; i++) {//直接一个一个看有没有流
if (e_[ask[i][0]].x < ask[i][2]) {
num++;
}
}
printf("%d\n", num);
for (int i = 1; i <= asktot; i++) {
if (e_[ask[i][0]].x < ask[i][2]) {
printf("%lld %lld %lld\n", e_[ask[i][0]].to, ask[i][1], ask[i][3]);
}
}
}
}
return 0;
}