CF587D Duff in Mafia 题解
安利\(:\) 杂题选做
首先忽略边权\(,\)考虑求一组可行的方案\(.\)可以使用\(2-SAT\)实现\(,\)建图需要前后缀优化\(.\)
加上边权的限制就是加一个二分\(.\)
强制让一个东西不能选就是加一条 \((x,1)->(x,0)\) 的边\(.\)
\(O(mlogm),\)实现的时候注意节省空间\(.\)
我的代码是\(|V|\leq 7m,\) \(|E| \leq 17m\)
关于前后缀优化\(:\)
考虑我把原图中的点都拿出来并排成一排\(p_1,p_2,..,p_m.\)
然后我需要快速实现若干次一个点\(u\)往这一排点连边或者往这一排点中除了一个点的其他点连边\(.\)
那么\(,\)可以建出辅助点\(pre_i\)和\(suf_i,\)分别表示连向\(p_{1..i}\)和连向\(p_{i..m}\)的点\(.\)
然后就可以连一个前缀\(,\)连一个后缀\(,\)就解决问题了\(.\)
一排点往点\(u\)连边的过程类似\(.\)
这个优化建图是线性的\(.\)
代码\(:\)
#include <bits/stdc++.h>
using namespace std;
template <typename T> void read(T &x){
x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
inline void write(int x){if (x > 9) write(x/10); putchar(x%10+'0'); }
const int N = 50005,M = 50005,V = N * 7,E = M * 17;
int cntv;
int He[V],Ne[E],To[E],_k;
inline void init(){ for (int i = 1; i <= cntv; ++i) He[i] = 0; _k = 0; }
inline void add(int x,int y){ ++_k; To[_k] = y,Ne[_k] = He[x],He[x] = _k; }
int Time,dfn[V],low[V],vis[V],stk[V],top,scc[V],scc_cnt;
inline void dfs(int x){
dfn[x] = low[x] = ++Time; stk[++top] = x; vis[x] = 1;
for (int y,p = He[x]; p ; p = Ne[p]){
y = To[p];
if (!dfn[y]) dfs(y),low[x] = min(low[x],low[y]);
else if (vis[y]) low[x] = min(low[x],dfn[y]);
}
if (low[x] == dfn[x]){
scc[x] = ++scc_cnt; vis[x] = 0;
while (stk[top] ^ x) scc[stk[top]] = scc[x],vis[stk[top]] = 0,--top;
--top;
}
}
inline void tarjan(){
int i;
Time = 0,top = 0,scc_cnt = 0;
for (i = 1; i <= cntv; ++i) dfn[i] = low[i] = vis[i] = scc[i] = stk[i] = 0;
for (i = 1; i <= cntv; ++i) if (!dfn[i]) dfs(i);
}
int n,m,ex[M],ey[M],ec[M],et[M];
int eex[E],eey[E],le;
vector<int>G[N];
inline void build(int w){
static int p[N],lenp,i,q[N],x,y;
lenp = G[w].size();
for (i = 1; i <= lenp; ++i) p[i] = G[w][i-1];
for (i = 2; i <= lenp; ++i){ q[i] = ++cntv; ++le; eex[le] = q[i],eey[le] = p[i]; }
for (i = 2; i < lenp; ++i){ ++le; eex[le] = q[i],eey[le] = q[i+1]; }
for (i = 1; i < lenp; ++i){ ++le; eex[le] = p[i]+m,eey[le] = q[i+1]; }
for (i = 1; i < lenp; ++i){ q[i] = ++cntv; ++le; eex[le] = q[i],eey[le] = p[i]; }
for (i = 2; i < lenp; ++i){ ++le; eex[le] = q[i],eey[le] = q[i-1]; }
for (i = 2; i <= lenp; ++i){ ++le; eex[le] = p[i]+m,eey[le] = q[i-1]; }
}
inline void work(){
int i,x,y;
map<pair<int,int> ,int>M; M.clear();
cntv = m<<1;
for (i = 1; i <= m; ++i){
G[ex[i]].push_back(i),G[ey[i]].push_back(i);
if (M.count(make_pair(ec[i],ex[i]))){
x = M[make_pair(ec[i],ex[i])],y = i;
++le; eex[le] = x,eey[le] = y+m;
++le; eex[le] = x+m,eey[le] = y;
++le; eex[le] = y,eey[le] = x+m;
++le; eex[le] = y+m,eey[le] = x;
}
else M[make_pair(ec[i],ex[i])] = i;
if (M.count(make_pair(ec[i],ey[i]))){
x = M[make_pair(ec[i],ey[i])],y = i;
++le; eex[le] = x,eey[le] = y+m;
++le; eex[le] = x+m,eey[le] = y;
++le; eex[le] = y,eey[le] = x+m;
++le; eex[le] = y+m,eey[le] = x;
}
else M[make_pair(ec[i],ey[i])] = i;
}
for (i = 1; i <= n; ++i) if (G[i].size() > 1) build(i);
}
inline bool check(int v,int tp){
int i;
init();
for (i = 1; i <= le; ++i) add(eex[i],eey[i]);
for (i = 1; i <= m; ++i) if (et[i] > v) add(i+m,i);
tarjan();
for (i = 1; i <= m; ++i) if (scc[i] == scc[i+m]) return 0;
if (!tp) return 1;
int cnt = 0;
for (i = 1; i <= m; ++i) cnt += scc[i+m] < scc[i];
write(v),putchar(' '),write(cnt),putchar('\n');
for (i = 1; i <= m; ++i) if (scc[i+m] < scc[i]) write(i),putchar((--cnt)?' ':'\n');
return 1;
}
namespace sub0{
int v[M],lv,cnt[N]; vector<int>G[N];
inline bool chk(){
int i,j,id,x,y;
for (i = 1; i <= m; ++i) v[i] = ec[i]; sort(v+1,v+m+1),lv = unique(v+1,v+m+1)-v-1;
for (i = 1; i <= m; ++i) ec[i] = lower_bound(v+1,v+lv+1,ec[i])-v,G[ec[i]].push_back(i);
for (i = 1; i <= m; ++i) if (G[i].size()){
for (j = 0; j < G[i].size(); ++j){
id = G[i][j],x = ex[id],y = ey[id],++cnt[x],++cnt[y];
if (cnt[x] > 2) return 0; if (cnt[y] > 2) return 0;
}
for (j = 0; j < G[i].size(); ++j) id = G[i][j],x = ex[id],y = ey[id],--cnt[x],--cnt[y];
}
return 1;
}
}
int main(){
int i;
read(n),read(m);
for (i = 1; i <= m; ++i) read(ex[i]),read(ey[i]),read(ec[i]),read(et[i]);
if (!sub0::chk()){ puts("No"); return 0; }
work();
int L = 0,R = 1e9 + 7,Mid,Ans = R;
while (L <= R){ Mid = L+R>>1; if (check(Mid,0)) Ans = Mid,R = Mid - 1; else L = Mid + 1; }
if (Ans > 1000000000) puts("No"); else puts("Yes"),check(Ans,1);
return 0;
}