同桌的你(环套树)(DP)

同桌的你

题目大意

给你一个 n 个点 m 个边的图,每个点一定会至少有一条边。
然后要你选尽可能多的点对,使得每个点对之间都有边。
然后每个点有 1/2 的点权,你在选点对最多的基础上,还要让点权不同的点对尽可能多。
然后要输出其中一种方案。

思路

首先你考虑看到这个图应该是一些连通块,每个连通块只会有一个环。

那就是一个环套树森林。
那你考虑分别解决每个森林。
然后你发现因为要最多点对所以在环上的相邻两个点如果选了不优一定是因为有一个点跟其他匹配了。
那你考虑找环上相邻的两条边,分别断掉,然后做 DP,然后选最优的那个作为解。

然后讲一下如何 DP,设 fu,0/1 为搞定 i 子树,i 不配对或者配对的最大分数。
然后 fu,0=v=sonumax{fv,0,fv,1}
然后 fu,1=v=sonumax{fu,0max{fv,0,fv,1}+fu,0+valu,v}

然后两个权值搞一下即可。
(不过有一种玩法就是线段树加很大边权的思路,你把首要条件弄成 1e9,次要弄成 1,然后求出来的答案 /1e9 就是首要条件的答案,mod 1e9 就是次要条件的答案)

然后至于计算方案你就记录一下是从哪里转移过来的就行了。
然后要卡常。

代码

dfs 版(在可恶的会爆栈空间的 jzoj 上只能有 50 分)

#include<queue>//这里没有卡常,所以就算开了栈空间也可能会 TLE #include<cstdio> #include<vector> #include<cstring> #include<iostream> #define ll long long using namespace std; struct node { ll x; int to, nxt, rgt; }e[2000001]; int T, n, a[1000001], b[1000001], kill1, kill2; int fa[1000001], dfn[1000001], tmp; int low[1000001], in[1000001], remm; int sta[1000001], le[1000001], KK, st; int spc[5], totans, totjh, rem[2][1000001][2]; vector <int> jh[1000001], ansbh; ll f[2][1000001][2]; queue <int> q, qq, qqq; void add(int x, int y, ll z) { e[++KK] = (node){z, y, le[x], 1}; le[x] = KK; e[++KK] = (node){z, x, le[y], 0}; le[y] = KK; } int find(int now) { if (fa[now] == now) return now; return fa[now] = find(fa[now]); } void connect(int x, int y) { int X = find(x), Y = find(y); if (X == Y) return ; fa[X] = Y; } int DP(int now, int op, int father) { st = 0; for (int i = le[now]; i; i = e[i].nxt) if (e[i].to != father) { if (i == kill1 || i == kill2) continue; // if (now == spc[op + 1] && e[i].to == spc[op]) continue; DP(e[i].to, op, now); f[op][now][0] += max(f[op][e[i].to][1], f[op][e[i].to][0]);//先搞不选它的 } for (int i = le[now]; i; i = e[i].nxt) if (e[i].to != father) { if (i == kill1 || i == kill2) continue; // if (now == spc[op + 1] && e[i].to == spc[op]) continue; if (f[op][now][1] < f[op][now][0] - max(f[op][e[i].to][1], f[op][e[i].to][0]) + f[op][e[i].to][0] + e[i].x) { f[op][now][1] = f[op][now][0] - max(f[op][e[i].to][1], f[op][e[i].to][0]) + f[op][e[i].to][0] + e[i].x; rem[op][now][1] = e[i].to; }//然后选一个儿子跟它配对 } } void work() { for (int i = le[spc[1]]; i; i = e[i].nxt) if (e[i].rgt && e[i].to == spc[0]) { kill1 = i; kill2 = i + 1; break; } DP(spc[0], 0, 0); for (int i = le[spc[2]]; i; i = e[i].nxt) if (e[i].rgt && e[i].to == spc[1]) { kill1 = i; kill2 = i + 1; break; } DP(spc[0], 1, 0); ll x, y, xx, yy; if (f[0][spc[0]][0] > f[0][spc[0]][1]) x = f[0][spc[0]][0], xx = 0; else x = f[0][spc[0]][1], xx = 1; if (f[1][spc[0]][0] > f[1][spc[0]][1]) y = f[1][spc[0]][0], yy = 0; else y = f[1][spc[0]][1], yy = 1; if (x > y) {//两次 DP 的结果哪个优用哪个 int op = 0; totans += x / 1000000000; totjh += x % 1000000000; while (!q.empty()) q.pop(); while (!qq.empty()) qq.pop(); while (!qqq.empty()) qqq.pop(); q.push(spc[0]); qq.push(xx); qqq.push(0); while (!q.empty()) { for (int i = le[spc[1]]; i; i = e[i].nxt) if (e[i].rgt && e[i].to == spc[0]) { kill1 = i; kill2 = i + 1; break; } int now = q.front(); q.pop(); xx = qq.front(); qq.pop(); int fafa = qqq.front(); qqq.pop(); for (int i = le[now]; i; i = e[i].nxt)//找方案 if (e[i].to != fafa) { if (i == kill1 || i == kill2) continue; // if (now == spc[op + 1] && e[i].to == spc[op]) continue; if ((xx && e[i].to == rem[op][now][xx]) || f[op][e[i].to][1] < f[op][e[i].to][0]) { q.push(e[i].to); qq.push(0); qqq.push(now); } else { if (f[op][e[i].to][1]) { q.push(e[i].to); qq.push(1); qqq.push(now); } } if (xx && e[i].to == rem[op][now][xx]) { ansbh.push_back(now); ansbh.push_back(e[i].to); } } } } else { for (int i = le[spc[2]]; i; i = e[i].nxt) if (e[i].rgt && e[i].to == spc[1]) { kill1 = i; kill2 = i + 1; break; } int op = 1; totans += y / 1000000000; totjh += y % 1000000000; while (!q.empty()) q.pop(); q.push(spc[0]); q.push(yy); q.push(0); while (!q.empty()) { int now = q.front(); q.pop(); xx = q.front(); q.pop(); int fafa = q.front(); q.pop(); for (int i = le[now]; i; i = e[i].nxt) if (e[i].to != fafa) { if (i == kill1 || i == kill2) continue; // if (now == spc[op + 1] && e[i].to == spc[op] && e[i].rgt) continue; if ((xx && e[i].to == rem[op][now][xx]) || f[op][e[i].to][1] < f[op][e[i].to][0]) { q.push(e[i].to); q.push(0); q.push(now); } else { q.push(e[i].to); q.push(1); q.push(now); } if (xx && e[i].to == rem[op][now][xx]) { ansbh.push_back(now); ansbh.push_back(e[i].to); } } } } } void tarjan(int now) { dfn[now] = low[now] = ++tmp; sta[++sta[0]] = now; for (int i = le[now]; i; i = e[i].nxt) { if (!e[i].rgt) continue; if (!dfn[e[i].to]) { tarjan(e[i].to); low[now] = min(low[now], low[e[i].to]); } else if (!in[e[i].to]) low[now] = min(low[now], low[e[i].to]); } if (dfn[now] == low[now]) { in[now] = find(now); int temp = 0; while (sta[sta[0]] != now) { in[sta[sta[0]]] = find(now); if (temp < 3) { spc[temp] = sta[sta[0]]; temp++; } sta[0]--; } if (temp < 3) { spc[temp] = sta[sta[0]]; temp++; } sta[0]--; if (temp >= 3) { work(); } else if (temp == 2) { spc[temp] = spc[0]; temp++; work(); } } } int main() { // freopen("read.txt", "r", stdin); scanf("%d", &T); while (T--) { scanf("%d", &n); tmp = 0; sta[0] = 0; KK = 0; totans = 0; totjh = 0; ansbh.clear(); for (int i = 1; i <= n; i++) fa[i] = i, in[i] = 0, dfn[i] = 0, le[i] = 0, jh[i].clear(); memset(f, 0, sizeof(f)); memset(rem, 0, sizeof(rem)); for (int i = 1; i <= n; i++) { scanf("%d %d", &a[i], &b[i]); connect(a[i], i); } for (int i = 1; i <= n; i++) { add(i, a[i], 1000000000 + (b[i] != b[a[i]])); } for (int i = 1; i <= n; i++) jh[find(i)].push_back(i); for (int i = 1; i <= n; i++) if (!dfn[i]) { tarjan(i); } printf("%d %d\n", totans, totjh); for (int i = 0; i < totans; i++) { printf("%d %d\n", ansbh[i * 2], ansbh[i * 2 + 1]); } } return 0; }

bfs 版(找环不再用 tarjan,而是直接跳找)

#pragma GCC optimize(2)//加油吧!卡常人! #pragma GCC optimize(3) #pragma GCC optimize("Ofast") #pragma GCC optimize("inline") #pragma GCC optimize("-fgcse") #pragma GCC optimize("-fgcse-lm") #pragma GCC optimize("-fipa-sra") #pragma GCC optimize("-ftree-pre") #pragma GCC optimize("-ftree-vrp") #pragma GCC optimize("-fpeephole2") #pragma GCC optimize("-ffast-math") #pragma GCC optimize("-fsched-spec") #pragma GCC optimize("unroll-loops") #pragma GCC optimize("-falign-jumps") #pragma GCC optimize("-falign-loops") #pragma GCC optimize("-falign-labels") #pragma GCC optimize("-fdevirtualize") #pragma GCC optimize("-fcaller-saves") #pragma GCC optimize("-fcrossjumping") #pragma GCC optimize("-fthread-jumps") #pragma GCC optimize("-funroll-loops") #pragma GCC optimize("-fwhole-program") #pragma GCC optimize("-freorder-blocks") #pragma GCC optimize("-fschedule-insns") #pragma GCC optimize("inline-functions") #pragma GCC optimize("-ftree-tail-merge") #pragma GCC optimize("-fschedule-insns2") #pragma GCC optimize("-fstrict-aliasing") #pragma GCC optimize("-fstrict-overflow") #pragma GCC optimize("-falign-functions") #pragma GCC optimize("-fcse-skip-blocks") #pragma GCC optimize("-fcse-follow-jumps") #pragma GCC optimize("-fsched-interblock") #pragma GCC optimize("-fpartial-inlining") #pragma GCC optimize("no-stack-protector") #pragma GCC optimize("-freorder-functions") #pragma GCC optimize("-findirect-inlining") #pragma GCC optimize("-fhoist-adjacent-loads") #pragma GCC optimize("-frerun-cse-after-loop") #pragma GCC optimize("inline-small-functions") #pragma GCC optimize("-finline-small-functions") #pragma GCC optimize("-ftree-switch-conversion") #pragma GCC optimize("-foptimize-sibling-calls") #pragma GCC optimize("-fexpensive-optimizations") #pragma GCC optimize("-funsafe-loop-optimizations") #pragma GCC optimize("inline-functions-called-once") #pragma GCC optimize("-fdelete-null-pointer-checks") #include<queue> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #define ll long long #define rr register using namespace std; struct node { ll x; int to, nxt, rgt; }e[2000001]; int T, n, a[1000001], b[1000001], kill1, kill2; int dfn[1000001], tmp, xx, yy, temp, kill3, kill4; int le[1000001], KK, st; int spc[5], totans, totjh, rem[2][1000001]; int stac[1000001], Fa[1000001], re; vector <int> ansbh; ll f[2][1000001][2], x, y; queue <int> q; char c; void add(int x, int y, ll z) { e[++KK] = (node){z, y, le[x], 1}; le[x] = KK; e[++KK] = (node){z, x, le[y], 0}; le[y] = KK; } int DP(int now, int op, int father) { stac[0] = 0; stac[++stac[0]] = now; Fa[stac[0]] = father; for (rr int i = 1; i <= stac[0]; i++) { now = stac[i]; father = Fa[i]; for (int i = le[now]; i; i = e[i].nxt) if (e[i].to != father) { if (i == kill1 || i == kill2) continue; stac[++stac[0]] = e[i].to; Fa[stac[0]] = now; } } for (rr int i = stac[0]; i >= 1; i--) { now = stac[i]; father = Fa[i]; st = 0; for (int i = le[now]; i; i = e[i].nxt) if (e[i].to != father) { if (i == kill1 || i == kill2) continue; f[op][now][0] += max(f[op][e[i].to][1], f[op][e[i].to][0]); } for (int i = le[now]; i; i = e[i].nxt) if (e[i].to != father) { if (i == kill1 || i == kill2) continue; if (f[op][now][1] < f[op][now][0] - max(f[op][e[i].to][1], f[op][e[i].to][0]) + f[op][e[i].to][0] + e[i].x) { f[op][now][1] = f[op][now][0] - max(f[op][e[i].to][1], f[op][e[i].to][0]) + f[op][e[i].to][0] + e[i].x; rem[op][now] = e[i].to; } } } } ll xxx, yyy; int xxxx, yyyy, op, fafa; void work() { int now; for (int i = le[spc[0]]; i; i = e[i].nxt) if (e[i].rgt && e[i].to == spc[1]) { kill1 = i; kill2 = i + 1; break; } for (int i = le[spc[1]]; i; i = e[i].nxt) if (e[i].rgt && e[i].to == spc[2]) { kill3 = i; kill4 = i + 1; break; } DP(spc[0], 0, 0); swap(kill1, kill3); swap(kill2, kill4); DP(spc[0], 1, 0); if (f[0][spc[0]][0] > f[0][spc[0]][1]) xxx = f[0][spc[0]][0], xxxx = 0; else xxx = f[0][spc[0]][1], xxxx = 1; if (f[1][spc[0]][0] > f[1][spc[0]][1]) yyy = f[1][spc[0]][0], yyyy = 0; else yyy = f[1][spc[0]][1], yyyy = 1; if (xxx > yyy) { op = 0; totans += xxx / 1000000000; totjh += xxx % 1000000000; swap(kill1, kill3); swap(kill2, kill4); q.push(spc[0]); q.push(xxxx); q.push(0); while (!q.empty()) { int now = q.front(); q.pop(); xxxx = q.front(); q.pop(); int fafa = q.front(); q.pop(); for (int i = le[now]; i; i = e[i].nxt) if (e[i].to != fafa) { if (i == kill1 || i == kill2) continue; if ((xxxx && e[i].to == rem[op][now]) || f[op][e[i].to][1] < f[op][e[i].to][0]) { q.push(e[i].to); q.push(0); q.push(now); } else { if (f[op][e[i].to][1]) { q.push(e[i].to); q.push(1); q.push(now); } } if (xxxx && e[i].to == rem[op][now]) { ansbh.push_back(now); ansbh.push_back(e[i].to); } } } } else { op = 1; totans += yyy / 1000000000; totjh += yyy % 1000000000; q.push(spc[0]); q.push(yyyy); q.push(0); while (!q.empty()) { now = q.front(); q.pop(); yyyy = q.front(); q.pop(); fafa = q.front(); q.pop(); for (int i = le[now]; i; i = e[i].nxt) if (e[i].to != fafa) { if (i == kill1 || i == kill2) continue; if ((yyyy && e[i].to == rem[op][now]) || f[op][e[i].to][1] < f[op][e[i].to][0]) { q.push(e[i].to); q.push(0); q.push(now); } else { q.push(e[i].to); q.push(1); q.push(now); } if (yyyy && e[i].to == rem[op][now]) { ansbh.push_back(now); ansbh.push_back(e[i].to); } } } } } void get_cir(int now) { y = now; tmp++; for (y; !dfn[y]; y = a[y]) { dfn[y] = tmp; } temp = 0; spc[0] = y; temp++; spc[1] = a[y]; temp++; if (a[a[y]] != y) spc[2] = a[a[y]], temp++; if (temp >= 3) { work(); } else if (temp == 2) { spc[temp] = spc[0]; temp++; work(); } stac[0] = 0; stac[++stac[0]] = now; for (rr int ttt = 1; ttt <= stac[0]; ttt++) { int now = stac[ttt]; for (int i = le[now]; i; i = e[i].nxt) if (dfn[e[i].to] != -1) { dfn[e[i].to] = -1; stac[++stac[0]] = e[i].to; } } } char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf; #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) int read() { re = 0; c = getchar(); while (c < '0' || c > '9') c = getchar(); while (c >= '0' && c <= '9') { re = re * 10 + c - '0'; c = getchar(); } return re; } void write(int x) { if (x > 9) write(x / 10); putchar(x % 10 + '0'); } int main() { // freopen("read.txt", "r", stdin); // freopen("write.txt", "w", stdout); T = read(); while (T--) { n = read(); tmp = 0; KK = 0; totans = 0; totjh = 0; ansbh.clear(); for (rr int i = 1; i <= n; i++) { dfn[i] = 0, le[i] = 0; f[0][i][0] = f[0][i][1] = f[1][i][0] = f[1][i][1] = 0; rem[0][i] = rem[1][i] = 0; } for (rr int i = 1; i <= n; i++) { a[i] = read(); b[i] = read(); } for (rr int i = 1; i <= n; i++) { add(i, a[i], 1000000000 + (b[i] != b[a[i]])); } for (rr int i = 1; i <= n; i++) if (dfn[i] != -1) { get_cir(i); } write(totans); putchar(' '); write(totjh); putchar('\n'); for (rr int i = 0; i < totans; i++) { write(ansbh[i << 1]); putchar(' '); write(ansbh[i << 1 | 1]); putchar('\n'); } } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/jzoj_4760.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(36)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示