题解:CF587D Duff in Mafia

· · 题解

最优解已经被我们占领拉

首先二分答案,将权值 \leq mid 的边叫做活边(可选可不选),全值 >mid 的边叫做死边(不可以选).

考虑分类讨论.

1.如果一个点连了 >2 条同色边一定无解

证明:选和不选两种状态,鸽巢原理得知至少有一个状态选了两条边,无解.

2.如果一个点连了 2 条同色死边一定无解

似乎不需要证明.

3.如果一个点对于两种不同颜色都有 2 条边,那么一定无解

证明:每一种颜色只能选一条边不选一条边,那么这个点就会被选两次.

(具体可以自行画图证明)

那么我们对于一个点所连的边就只有三种情况.

1.一个同色活边+死边,若干不同色活边死边

此时这个同色的活边必选,所以其他边全都不能要.

2.两个同色活边,若干不同色活边死边

此时只能二选一,跟其他边也没关系,也全都不要.

3.若干不同色活边死边

没有同色边意味着可以选这些边中的至多一个.

(具体可以自行画图理解)

用 2-SAT 算法可以实现.

但是注意到情况三需要边两两加限制,时间复杂度为 m^2 ,难以通过.

这个时候正常人会掏出前缀优化见图,但是我不会.

但是这样常数翻了个若干倍.

我们将 2-SAT 中的连边称作限制.

那么存在定理.

4.无限制的边一定可以不选

证明:

假设不选它之后会冲突.

那么说明有一个同颜色共顶点的边也没选.

可是如果存在同颜色共顶点的边,那么要么是无解要么是情况一或二,存在限制.

与条件矛盾.

所以我们把没有限制的边也全不要,剩下的边的数量很少.

暴力添加限制可做.

代码:(358ms 最优解榜三)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int n,m;
struct edge{int u,v,c,w,id;}e[N];
bool cmp_w(edge a,edge b){return a.w<b.w;}
vector<int> H,G[N];
struct node{
    int v,c,id;
    node(int vv=0,int cc=0,int iid=0){v=vv; c=cc; id=iid;}
};
vector<node> T[N];
void read(){
    map<int,int> mp;
    mp.clear();
    int num=0;
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int col;
        cin>>e[i].u>>e[i].v>>col>>e[i].w;
        e[i].id=i;
        if(!mp[col]) mp[col]=++num;
        e[i].c=mp[col];
    }
    sort(e+1,e+1+m,cmp_w);
    for(int i=1;i<=m;i++){
        T[e[i].u].push_back(node(e[i].v,e[i].c,i));
        T[e[i].v].push_back(node(e[i].u,e[i].c,i));
    }
}
namespace tarjan{
    int tot,dfn[N],low[N];
    int top,num,st[N],scc[N],act[N];
    void dfs(int x){
        act[x]=1; st[++top]=x;
        dfn[x]=++tot; low[x]=tot;
        for(int y:G[x]){
            if(!dfn[y]){
                dfs(y);
                low[x]=min(low[x],low[y]);
            }else if(act[y]) low[x]=min(low[x],dfn[y]);
        }
        if(dfn[x]==low[x]){
            ++num;
            while(1){
                int u=st[top]; top--;
                act[u]=0; scc[u]=num;
                if(u==x) break;
            }
        }
    }
    void clear(){
        top=num=tot=0;
        for(int i=1;i<=m+m;i++){
            dfn[i]=low[i]=scc[i]=act[i]=0;
        }
    }
    bool sol(){
        clear();
        for(int i=m+m;i>=1;i--) if(!dfn[i]) dfs(i);
        for(int i=1;i<=m;i++) if(scc[i]==scc[i+m]) return 0;
        H.clear();
        for(int i=1;i<=m;i++) if(scc[i]<scc[i+m]) H.push_back(i);
        return 1;
    }
}
namespace build{
    vector<int> A[N];
    int id,cnt,num,Max,col,box[N],g[N],f[N];
    void add(int x,int y){G[x].push_back(y);}
    void clear(){
        for(int i=1;i<=m;i++) box[i]=0;
        for(int i=1;i<=n;i++) f[i]=0,A[i].clear();
        for(int i=1;i<=m+m;i++) G[i].clear();
        for(int i=1;i<=m;i++) g[i]=0;
    }
    void upd(int c){
        box[c]++;
        if(box[c]>Max) Max=box[c],col=c,cnt=1;
        else if(box[c]==Max) cnt++;
    }
    bool sol1(){
        for(int i=id+1;i<=m;i++) add(i,i+m);
        for(int x=1;x<=n;x++){
            Max=cnt=col=num=0;
            for(node p:T[x]) upd(p.c);
            if(Max>2) return 0;
            if(Max==2&&cnt>1) return 0;

            int idx=0,idy=0,idz=0,u,v;
            for(node p:T[x])if(col==p.c){
                if(p.id>id) num++,idy=p.id;
                else{
                    if(idx==0) idx=p.id,u=p.v;
                    else idz=p.id,v=p.v;
                }
            }

            if(Max==2){
                if(num==2) return 0;
                if(num==1){
                    g[idx]=1; f[u]=idx;
                    add(idx+m,idx);
                    // A[u].push_back(idx);
                }else{
                    g[idx]=g[idz]=1;
                    add(idx+m,idz);
                    add(idx,idz+m);
                    add(idz,idx+m);
                    add(idz+m,idx);
                    // A[u].push_back(idx);
                    // A[v].push_back(idz);
                }
            }
            for(node p:T[x]) box[p.c]=0;
        }
        for(int i=1;i<=m;i++) if(g[i]==1){
            A[e[i].u].push_back(i);
            A[e[i].v].push_back(i);
        }
        for(int x=1;x<=n;x++){
            if(f[x]){
                for(node i:T[x]) if(i.id!=f[x])add(i.id,i.id+m);
                continue;
            }
            for(int i:A[x]) for(int j:A[x]) if(i!=j){
                add(i,j+m);
                add(j,i+m);
            }
        }
        return 1;
    }
    bool work(int nzq){
        id=nzq;
        clear();
        if(!sol1()) return 0;
        return 1;
    }
}
bool check(int id){
    if(!build::work(id)) return 0;
    if(!tarjan::sol()) return 0;
    return 1;
}
void work(){
    int L=0,R=m+1;
    while(L<R){
        int mid=(L+R)>>1;
        if(check(mid)) R=mid;
        else L=mid+1;
    }
    if(L==m+1){
        cout<<"No\n";
    }else{
        cout<<"Yes\n";
        check(L);
        cout<<e[L].w<<' '<<H.size()<<'\n';
        for(int p:H) cout<<e[p].id<<' ';
        cout<<'\n';
    }
}
int main(){
    ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    // freopen("nzq.in","r",stdin);
    // freopen("nzq.out","w",stdout);
    read();
    work();
    return 0;
}