CF tracker
Edu List
https://codeforces.com/contest/1721
F. Matching Reduction 给2分图, 左侧n1个点, 右侧n2个点, m条边
2种询问
第一种, 删除最少的点, 让最大匹配的值 正好少1, 输出具体选点方案, 剩余的匹配边的index和
第二种, (只会紧跟着第一种, 输出实际的匹配方案)
注意的是,在输出询问的结果前是无法读下一个的, 你需要fflush 输出后才能读下一个询问
范围 n1,n2 [1,2e5]
m 2e5
q 2e5
8s
512mb
强制在线
我的思路 极端一点, 不妨设每次都有1和2的询问, 就变成了如何维护二分图的匹配了
有点想到 abc215 的霍尔定理(左侧n点,右侧m点, 最大匹配为n的充分必要条件, 左侧的任意k个点集子集,连右侧点的个数都>=k)
所以如果 当前的匹配 刚好一侧点=匹配个数, 那么这一侧就随便删除一个点就行, 并且还满足这一侧的点=匹配数
问题来到 当匹配数 < 左侧点, 右侧点 时如何处理
1 2 3 4 1 -> 4 2 -> 4 3 -> 5 3 -> 6
比如这个就是两侧都是3个点,但是最大匹配只是2, 看上去, 删除3或者4都能让个数-1
再变化
1 2 3 4 5 6 1 -> 4 2 -> 4 3 -> 5 3 -> 6 7 -> 8 7 -> 9
不论删除3还是7 都可以让匹配数-1, 但是 都不会让匹配数 = 一侧点数
主要感觉霍尔定理还是和完美匹配挂钩
考虑直接匹配, 得到一个方案, 如果删除的点不在匹配中, 那么一定对个数无影响
所以至少需要删除一个匹配中的点
虽然匹配过程有顺序, 但是因为点是可以乱序, 所以考虑如果让最后一个失配 会怎么样
删除左侧点,
那么 左侧且在它前面的未匹配点不会影响,因为没匹配它时也未找到方案(在匈牙利算法中一个匹配成功的点会一直在匹配中)
左侧未匹配在它之后的可能会有影响, 设它是 u0 -> v0, 而后面的是u1 能通过某条路径(不一定直接,可能和前面的点走反边)和v0 匹配, 而u0 可以匹配某个当前右侧未匹配的 v, 那么这种情况 会有更大匹配, 而当前就是最大匹配矛盾,
所以如果有u1有路径和v0 匹配则 u0 一定没有额外可匹配的点,(这种情况删除v0即可, 空出来的u0不会被任何匹配)
否则所有u1都没有路径到v0, (这种情况删除u0即可,空出来的v0不会被任何匹配,其实左右交换是有对称性的)
综上得到, 每次只需要删除一个点(左右中一个和另一侧 未配点没有匹配关系的)
how 找
好处是上面的方案 所有匹配都没有变化, 只是减少, 而不会交换匹配的点
那么不如先跑一遍最大匹配, 然后枚举点和边, 计算每个点和未匹配的点有边的个数(O(E+V))
丢进set< pair<个数,点id>>
维护
然后删除时更新一下set
, 每条边更新一次, O(E log E)
然后随便set维护一下匹配方案
似乎就没了?
但是想了个反例
然后当前是1-1
和2-2
, 注意到右侧2没有未匹配点和它相连, 所以如果删除它匹配的左侧1, 并不能让个数下降
所以需要 找到的是一侧匹配的点 有, 一侧没有的这种去删除, 否则 所有的点都没有额外连接点了(就可以任意一个)
代码 https://codeforces.com/contest/1721/submission/189287144
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 #include <bits/stdc++.h> using namespace std;typedef long long ll;#define rep(i,a,n) for (int i=a;i<(int)n;i++) #define per(i,a,n) for (int i=n;i-->(int)a;) int read () {int r;scanf ("%intd" ,&r);return r;} int n;vector<pair<int ,int > > g[400010 ]; bool vis[400010 ];array<int ,2> u2ve[400010 ]; bool del[400010 ];int cnt[400010 ]; bool dfs (int u) { vis[u]=true ; for (auto [v,idx]:g[u]) if (!vis[v]){ vis[v]=true ; if (!u2ve[v][0 ] or dfs (u2ve[v][0 ])) { u2ve[v]={u,idx}; return true ; } } return false ; } int main () { int n1 = read (); int n2 = read (); int m = read (); int q = read (); rep (i,0 ,m){ int x = read (); int y = n1+read (); g[x].push_back ({y,i+1 }); g[y].push_back ({x,i+1 }); } rep (l,1 ,n1+1 ) if (dfs (l)) fill (vis+1 ,vis+n1+n2+1 ,false ); set<int > me; set<pair<int ,int >, greater<pair<int ,int >> > ci; ll ans=0 ; rep (r,n1+1 ,n1+n2+1 ) if (u2ve[r][0 ]) { auto [l,eid]=u2ve[r]; ans+=eid; u2ve[l]={r,eid}; me.insert (eid); } rep (u,1 ,n1+n2+1 ) if (u2ve[u][0 ]){ int c=0 ; for (auto [v,eid]:g[u]) c+= !u2ve[v][0 ]; ci.insert ({cnt[u]=c,u}); } auto update=[&](int u){ for (auto [v,eid]:g[u]) { ci.erase ({cnt[v],v}); cnt[v]++; ci.insert ({cnt[v],v}); } }; while (q--){ int op = read (); if (op == 1 ){ while (true ){ auto [c,u]=*ci.begin (); ci.erase (ci.begin ()); auto [v,eid]=u2ve[u]; if (me.count (eid)){ printf ("1\n" ); if (u<=n1) printf ("%d\n" ,u); else printf ("-%d\n" ,u-n1); ans-=eid; printf ("%lld\n" ,ans); me.erase (eid); update (v); break ; } } }else { printf ("%d\n" ,(int )me.size ()); for (auto eid:me) printf ("%d " ,eid); printf ("\n" ); } fflush (stdout); } return 0 ; }
题解 先找一个最大匹配
总结 当时比赛时间不够了, 思路还是有的
F
看来是属于 时间够就能过的题
参考 官方
hall’s theorem