模板——图论

缩点(强连通分量)

点击查看代码

const int N=1e5+5,inf=1e9;
vector<int> a[N];
stack<int> stk;
bool vis[N],instk[N];
int dfn[N],low[N],col[N],w[N]; // co:染色结果,w:点权
vector<int> sz; // sz:第i个颜色的点数
int n,m,dcnt;//
void dfs(int x){ // Tarjan求强联通分量
    vis[x]=instk[x]=1; stk.push(x);
    dfn[x]=low[x]=++dcnt;
    for(auto p:a[x]){
        if(!vis[p])dfs(p);
        if(instk[p])low[x]=min(low[x],low[p]);
    }
    if(low[x]==dfn[x]){
        int t; sz.push_back(0); // 记录
        do{
            t=stk.top();
            stk.pop();
            instk[t]=0;
            sz.back()+=w[t]; // 记录
            col[t]=sz.size(); // 染色
        }while(t!=x);
    }
}
void getscc(){
    fill(vis,vis+n,0);
    sz.clear();
    for(int i=1;i<=n;i++) if(!vis[i])dfs(i);
}
struct pii{
    int u,v;
};
void shrink(){ // 缩点,在a里重构
    vector<pii> tmp;
    getscc();
    for(int i=1;i<=n;i++) {
        for (auto j: a[i]) if (col[i] != col[j]) {
                pii u = {col[i], col[j]};
                tmp.push_back(u);
            }
    }
    n=sz.size();
    for(int i=1;i<=n;i++){
        a[i].clear();
        w[i]=sz[i];
    }
    for(auto i:tmp){
        a[i.u].push_back(i.v);
    }
}

最大流+输出方案

点击查看代码

struct FLOW{
    struct edge{int to,w,nxt;};
    vector<edge> a; int head[N],cur[N];
    int n,s,t;
    queue<int> q; bool inque[N];
    int dep[N];
    void ae(int x,int y,int w){ // add edge
        //cout<<"ae:"<<x<<" "<<y<<" "<<w<<endl;
        a.push_back({y,w,head[x]});
        head[x]=a.size()-1;
    }
    bool bfs(){ // get dep[]
        fill(dep,dep+n,inf); dep[s]=0;
        copy(head,head+n,cur);
        q=queue<int>(); q.push(s);
        while(!q.empty()){
            int x=q.front(); q.pop(); inque[x]=0;
            for(int i=head[x];i!=-1;i=a[i].nxt){
                int p=a[i].to;
                if(dep[p]>dep[x]+1 && a[i].w){
                    dep[p]=dep[x]+1;
                    if(inque[p]==0){
                        inque[p]=1;
                        q.push(p);
                    }
                }
            }
        }
        return dep[t]!=inf;
    }
    int dfs(int x,int flow){ // extend
        int now,ans=0;
        if(x==t)return flow;
        for(int &i=cur[x];i!=-1;i=a[i].nxt){
            int p=a[i].to;
            if(a[i].w && dep[p]==dep[x]+1)
                if((now=dfs(p,min(flow,a[i].w)))){
                    a[i].w-=now;
                    a[i^1].w+=now;
                    ans+=now,flow-=now;
                    if(flow==0)break;
                }
        }
        return ans;
    }
    bool is[N];
    void init(int _n){
        n=_n+1; a.clear();
        fill(head,head+n,-1);
        fill(inque,inque+n,0);
        fill(is,is+n,0);
    }
    int solve(int _s,int _t,int _n){ // return max flow
        s=_s,t=_t;
        int ans=0;
        while(bfs()) ans+=dfs(s,inf);
        for(int e=head[s];e>=0;e=a[e].nxt) if(a[e^1].w) is[a[e].to]=1;
        for(int e=head[t];e>=0;e=a[e].nxt) if(a[e].w){
            int v=a[e].to,u=v;
            while(1){
                if(u>=1 && u<=_n && is[u]){
                    is[u]=0;
                    break;
                }
                int w=0,tmp=0;
                for(int i=head[u];i>=0;i=a[i].nxt) if(i&1 && a[i].w){
                    w=a[i].to;
                    tmp=i;
                    break;
                }
                if(!w) break;
                a[tmp].w--;
                u=w;
            }
            //cout<<u<<" "<<v-_n<<endl;
           // fa[find(u)]=find(v-_n);
        }
        return ans;
    }
}flow;
void add(int x,int y,int w){flow.ae(x,y,w),flow.ae(y,x,0);}

2-sat

1.暴力dfs

判断是否有解/找字典序最小解——\(O(VE)\)(实际远小于上界)
按字典序找出前\(k\)个方案——\(O(kVE)\)(甚至能通过\(k,V2000\)的稠密图)

点击查看代码
struct twosat{ // 暴力版
    int n;
    vector<int> g[N*2];
    bool mark[N*2]; // mark即结果,表示是否选择了这个点
    int s[N],c;
    bool dfs(int x){ // private
        if(mark[x^1])return 0;
        if(mark[x])return 1;
        mark[s[c++]=x]=1;
        for(auto p:g[x])
            if(!dfs(p))
                return 0;
        return 1;
    }
    void init(int _n){
        n=_n;
        for(int i=0;i<n*2;i++){
            g[i].clear();
            mark[i]=0;
        }
    }
    int cnt=0;
    void solve(int pos){ // 按照字典序寻每一个解
        if(pos==n){
            cnt++;
            //cout<<"cnt="<<cnt<<endl;
            if(cnt>tot){
                puts("-1");
                exit(0);
            }
            //while(c>0)mark[s[--c]]=0;
            return;
        }
        int u=pos*2,v=u+1;
        if(mark[u] || mark[v]){
            solve(pos+1);
            return;
        }

        int tmp=c;
        if(dfs(u)) solve(pos+1);
        while(c>tmp) mark[s[--c]]=0;

        tmp=c;
        if(dfs(v)) solve(pos+1);
        while(c>tmp) mark[s[--c]]=0;

    }
    /*
    bool solve(){ // 返回是否存在解
        for(int i=0;i<n*2;i+=2)
            if(!mark[i] && !mark[i^1]){
                c=0;
                if(!dfs(i)){
                    while(c>0)mark[s[--c]]=0;
                    if(!dfs(i^1))return 0;
                }

            }
        return 1;
    }
     */
}ts;

2.(利用对称性)缩点

判断是否有解/找一个解——\(O(V+E)\)

3.bitset优化

优化两个过程:
1.求传递闭包
2.dfs找答案的判断(把O(E)变成O(V/bitset))
而dfs找答案的优化建立在求传递闭包的基础上,适用于找多个方案
按字典序找出前\(k\)个方案——\(O((kV^2+V^3)/bitset)\)(实际不比暴力快太多且码量大增,作为暴力被卡的替代方案)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 4005;
int n,m;
vector<int> v[N];int dfn[N],low[N],scc[N],sidx,stk[N],top;
bool instk[N];
void tarjan(int pos)
{
    static int didx = 0;dfn[pos] = low[pos] = ++didx;stk[++top] = pos;instk[pos] = 1;
    for (auto &i : v[pos])
        if (!dfn[i]) tarjan(i),low[pos] = min(low[pos],low[i]);
        else if (instk[i]) low[pos] = min(low[pos],dfn[i]);
    if (dfn[pos] == low[pos])
    {
        scc[pos] = ++sidx;instk[pos] = 0;
        while (stk[top] != pos) {int nd = stk[top--];instk[nd] = 0;scc[nd] = sidx;}
        --top;
    }
}
int cc;bitset<N> e[N],re[N],cov,vis;
void dfs(int pos)
{
    if (pos == m + 1)
    {
        if (++cc > n) {cout << -1 << endl;exit(0);}
        return;
    }
    assert(!cov[scc[pos * 2]] || !cov[scc[pos * 2 + 1]]);
    if (cov[scc[pos * 2]] || cov[scc[pos * 2 + 1]]) return dfs(pos + 1);
    if ((cov & re[scc[pos * 2 + 1]]).none() && (vis & e[scc[pos * 2]]).none())
    {
        auto t1 = cov,t2 = vis;cov |= e[scc[pos * 2]];vis |= re[scc[pos * 2 + 1]];
        dfs(pos + 1);
        cov = t1;vis = t2;
    }
    if ((cov & re[scc[pos * 2]]).none() && (vis & e[scc[pos * 2 + 1]]).none())
    {
        auto t1 = cov,t2 = vis;cov |= e[scc[pos * 2 + 1]];vis |= re[scc[pos * 2]];
        dfs(pos + 1);
        cov = t1;vis = t2;
    }
}
bool del[N];
int main ()
{
    cin >> n >> m;
    //...(build_edge)

    for (int i = 2;i <= m * 2 + 1;i++) if (!dfn[i]) tarjan(i);
    //build
    for (int i = 2;i <= m * 2 + 1;i++) assert(scc[i] != scc[i ^ 1]);
    //e,re
    for (int i = 1;i <= sidx;i++) e[i][i] = 1;
    for (int i = 2;i <= m * 2 + 1;i++)
        for (auto &j : v[i]) e[scc[i]][scc[j]] = 1;
    for (int i = 1;i <= sidx;i++)
        for (int j = 1;j <= sidx;j++) if (e[j][i]) e[j] |= e[i];
    for (int i = 1;i <= sidx;i++)
        for (int j = 1;j <= sidx;j++) if (e[i][j]) re[j][i] = 1;
    //cov,vis
    for (int i = 1;i <= m;i++)
        if (e[scc[i * 2]][scc[i * 2 + 1]]) cov[scc[i * 2 + 1]] = vis[scc[i * 2]] = 1;
        else if (e[scc[i * 2 + 1]][scc[i * 2]]) cov[scc[i * 2]] = vis[scc[i * 2 + 1]] = 1;
    dfs(1);
    return 0;
}
posted @ 2023-04-21 17:41  sz[sz]  阅读(18)  评论(0编辑  收藏  举报