20241221北京总结

授课内容:图论 , 网络流

我在网络流方面相当缺失 , 回去之后要先补一补

图论很难说有一些具体算法,主要是思想,trick 之类的,多见见题就好了

今天主要了解差分约束 , 同余最短路 , 联通性问题(强连通分量 , 边双 , 点双 )

P3275 [SCOI2011] 糖果

差分约束板子 , 但直接跑会 \(T\) , 所以把零环缩一下点就行

#include<bits/stdc++.h>
using namespace std;
struct edge{
	int nxt,to,len;
}e[1100000];
int head[1100000],cnt;
long long n,k,ans;
int dis[1100000];
int use[1100000];
bool vis[1100000];
void add(int x,int y,int len){
	e[++cnt].to=y;
	e[cnt].nxt=head[x];
	e[cnt].len=len;
	head[x]=cnt;
}
queue<int> q;
int main(){
	cin>>n>>k;
	for(int i=1;i<=k;i++){
		int x,a,b;
		cin>>x>>a>>b;
		if(x==1){
			add(a,b,0);
			add(b,a,0);
		}
		if(x==2){
			add(a,b,1);
		}
		if(x==3){
			add(b,a,0);
		}
		if(x==4){
			add(b,a,1);
		}
		if(x==5){
			add(a,b,0);
		}
	}
	for(int i=1;i<=n;i++) {
        vis[i]=true;
        dis[i]=1;
        use[i]=1;
        q.push(i);
    }
    int tot=0;
	while(!q.empty()){
        tot++;
        if(tot>2e7){
            cout<<-1<<'\n';
            return 0;
        }
		int now=q.front();
		q.pop();
		vis[now]=false;
		use[now]=0;
		for(int i=head[now];i;i=e[i].nxt){
			int to=e[i].to;
			if(dis[to]<dis[now]+e[i].len){
				use[i]++;
				if(use[i]==n-1){
					cout<<-1<<'\n';
                return 0;
				}
				dis[to]=dis[now]+e[i].len;
				if(!vis[to]){
					q.push(to);
					vis[to]=1;
				}
			}
		}
	}
	for(int i=1;i<=n;i++){
		ans=ans+dis[i];
	}
    cout<<ans;
	return 0;
}

CF19EFairy

就是删奇环 , 考虑到 \(dfs\) 树环都是返祖边的优良性质 , 就直接先把 \(dfs\) 树跑出来 , 找到返祖边上的奇环 .

由于环的可加性 , 我们这条边在过所有奇环的同时一个偶环也过不了 , 于是就考虑树上差分 , 通过链上加减来维护

/*
 * @Author: 2019yyy
 * @Date: 2024-12-21 08:13:23
 * @LastEditors: 2019yyy
 * @LastEditTime: 2024-12-21 16:35:37
 * @FilePath: \code\20241221\CF19E.cpp
 * @Description: 
 * 
 * I love Chtholly forever 
 */
#include<bits/stdc++.h>
using namespace std;
struct Edge{
    int next,to;
} a[1100000];
int cnt=1,head[11000000];
void addEdge(int x,int y){
    a[++cnt].next=head[x];
    a[cnt].to=y;
    head[x]=cnt;
}
int fa[1100000][21];
int f[1100000],dep[1100000],id[1100000];
void dfs(int x,int father){
    for(int i=head[x];i;i=a[i].next){
        int to=a[i].to;
        if(to==father or dep[to]){
            continue;
        }
        fa[to][0]=x;
        dep[to]=dep[x]+1;
        id[to]=i/2;
        dfs(to,x);
    }
}
int anscnt=0;
int lca(int x,int y){
    if(dep[x]!=dep[y]){
        if(dep[y]>dep[x]){
            swap(x,y);
        }
        for(int i=20;i>=0;i--){
            if(dep[fa[x][i]]>=dep[y]){
                x=fa[x][i];
            }
        }
    }
    if(x==y){
        return x;
    }
    for(int i=20;i>=0;i--){
        if(fa[x][i]!=fa[y][i]){
            x=fa[x][i];
            y=fa[y][i];
        }
    }
    return fa[x][0];
}
bool vis[1100000];
int h[1100000];
void dfs1(int x,int father){
    vis[x]=true;
    for(int i=head[x];i;i=a[i].next){
        int to=a[i].to;
        if((i xor 1)==father){
            continue;
        }
        if(vis[to]){
            if(dep[x]<=dep[to]){
                continue;
            }
            if((dep[x]-dep[to]) bitand 1){
                f[x]-=1;
                f[to]-=1;
                h[i/2]--;
                f[lca(x,to)]+=2;
            }else{
                f[x]+=1;
                f[to]+=1;
                h[i/2]++;
                f[lca(x,to)]-=2;
                anscnt++;
            }
        }else{
            dfs1(to,i);
        }
    }
}
bool vis2[1100000],flag;
int g[1100000],ans,res[1100000];
void dfs2(int x,int father){
    vis2[x]=true;
    g[x]=f[x];
    for(int i=head[x];i;i=a[i].next){
        int to=a[i].to;
        if((i xor 1)==father){
            continue;
        }
        if(vis2[to]){
            if(dep[x]<=dep[to]){
                continue;
            }
            if(h[i/2]==anscnt){
                res[++ans]=i/2;
            }
            continue;
        }
        dfs2(to,father);
        g[x]+=g[to];
    }
    if(g[x]==anscnt){
        if(id[x]!=0){
            res[++ans]=id[x];
        }
    }
}
int main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int x,y;
        cin>>x>>y;
        addEdge(x,y);
        addEdge(y,x);
    }
    for(int i=1;i<=n;i++){
        if(not dep[i]){
            dep[i]=1;
            dfs(i,0);
        }
    }
    
    for(int j=1;j<=20;j++){
        for(int i=1;i<=n;i++){
            fa[i][j]=fa[fa[i][j-1]][j-1];
        }
    }
    int t=0;
    for(int i=1;i<=n;i++){
        if(not vis[i]){
            anscnt=0,flag=false;
            dfs1(i,-1);
            t=t+anscnt;
            if(anscnt!=0){
                dfs2(i,-1);
            }
        }
    }
    if(t==0){
        cout<<m<<'\n';
        for(int i=1;i<=m;i++){
            cout<<i<<" ";
        }
        return 0;
    }
    sort(res+1,res+ans+1);
    ans=unique(res+1,res+ans+1)-(res+1);
    cout<<ans<<'\n';
    for(int i=1;i<=ans;i++){
        cout<<res[i]<<' ';
    }
    return 0;
}

CF412DGiving Awards

简单考虑一下顺序发现是后续遍历 , 简单切掉

#include<bits/stdc++.h>
using namespace std;
struct addEdge{
    int next,to;
} a[1100000];
int head[1100000],cnt;
void addEdge(int x,int y){
    a[++cnt].to=y;
    a[cnt].next=head[x];
    head[x]=cnt;
}
bool vis[1100000];
void dfs(int x){
    vis[x]=true;
    for(int i=head[x];i;i=a[i].next){
        int to=a[i].to;
        if(vis[to]){
            continue;
        }
        dfs(to);
    }
    cout<<x<<" ";
}
int main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int x,y;
        cin>>x>>y;
        addEdge(x,y);
    }
    for(int i=1;i<=n;i++){
        if(not vis[i]){
            dfs(i);
        }
    }
    cout<<'\n';
    return 0;
}

P7428 [THUPC2017] 母亲节的礼物

不太好做 , 贺了一下题解发现一定有解 , 所以直接上随机化

先随机一个颜色 , 再处理所有不合法的 (放到一个队列里) , 然后处理不合法的 , 把不合法的改变一下颜色

/*
 * @Author: 2019yyy
 * @Date: 2024-12-21 21:14:14
 * @LastEditors: 2019yyy
 * @LastEditTime: 2024-12-21 22:00:54
 * @FilePath: \code\20241221\P7428.cpp
 * @Description: 
 * 
 * I love Chtholly forever 
 */
#include<bits/stdc++.h>
using namespace std;
struct addEdge{
    int next,to;
} a[1100000];
int head[1100000],cnt;
void addEdge(int x,int y){
    a[++cnt].to=y;
    a[cnt].next=head[x];
    head[x]=cnt;
}
int col[1100000];
int fun(int x,int co){
    int res=0;
	for(int i=head[x];i;i=a[i].next){
		if(co==col[a[i].to]){++res;}
	}
	return res;
}
queue<int> q;
int main(){
    srand(time(0));
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T;
    cin>>T;
    while(T--){
        memset(head,0,sizeof(head));
        int n,m;
        cin>>n>>m;
        cnt=0;
        for(int i=1;i<=m;i++){
            int x,y;
            cin>>x>>y;
            addEdge(x,y);
            addEdge(y,x);
        }
        while(!q.empty()){
            q.pop();
        }
        for(int i=1;i<=n;i++){
            col[i]=rand()%4;
            if(fun(i,col[i])>1){
                q.push(i);
            }
        }
        while(not q.empty()){
            int now=q.front();
            q.pop();
            if(fun(now,col[now])<=1){
                continue;
            }
            int minn=0x3f3f3f3f;
            for(int i=0;i<4;++i){
				int t=fun(now,i);
				if(t<minn){
					minn=t;
					col[now]=i;
				}
			}
            for(int i=head[now];i;i=a[i].next){
                int to=a[i].to;
                if(col[to]==col[now] and fun(to,col[now])>1){
                    q.push(to);
                }
            }
        }
        for(int i=1;i<=n;i++){
            cout<<(char)('a'+col[i]);
        }
        cout<<'\n';
    }
}

P4819 [中山市选] 杀人游戏

容易发现一个 \(SCC\) 里的所有点都能互相监督 , 进一个不是杀人犯的点就 \(win\) 了 , 所以直接缩点

完了考虑不在缩点后的 \(DAG\) 怎么做 , 简单的想 , 若是监督了一个入度为 \(0\) 的点 , 紧接着与它相链接的点就被查明了 , 所以答案只与入读为 \(0\) 的点有关

注意特判(有很多)

/*
 * @Author: 2019yyy
 * @Date: 2024-12-21 19:18:55
 * @LastEditors: 2019yyy
 * @LastEditTime: 2024-12-21 21:11:04
 * @FilePath: \code\20241221\P4819.cpp
 * @Description: 
 * 
 * I love Chtholly forever 
 */
#include<bits/stdc++.h>
using namespace std;
struct Graph{
    struct Edge{
        int next,to;
    } a[3100000];
    int head[3100000],cnt;
    void addEdge(int x,int y){
        a[++cnt].to=y;
        a[cnt].next=head[x];
        head[x]=cnt;
    }
} g,f;
int dfscnt,colcnt,dfn[3100000],low[3100000],col[3100000],deg[3100000];
vector<int> scc[3100000];
bool vis[3100000];
stack<int> s;
void Tarjan(int x,int fa){
    dfn[x]=low[x]=++dfscnt;
    s.push(x);vis[x]=true;
    for(int i=g.head[x];i;i=g.a[i].next){
        int to=g.a[i].to;
        if(to==fa){
            continue;
        }
        if(not dfn[to]){
            Tarjan(to,x);
            low[x]=min(low[x],low[to]);
        }else{
            if(vis[to]){
                low[x]=min(low[x],dfn[to]);
            }
        }
    } 
    if(low[x]==dfn[x]){
        scc[++colcnt].push_back(x);
        col[x]=colcnt;vis[x]=false;
        while(not s.empty() and s.top()!=x){
            int now=s.top();
            scc[colcnt].push_back(now);
            s.pop();col[now]=colcnt;vis[now]=false;
        }
        s.pop();
    }
}
bool flag[1100000];
int main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int x,y;
        cin>>x>>y;
        g.addEdge(x,y);
    }
    if(n==1){ return cout<<"1.000000",0;}
    for(int i=1;i<=n;i++){
        if(not dfn[i]){
            Tarjan(i,0);
        }
    }
    for(int i=1;i<=colcnt;i++){
        for(auto j:scc[i]){
            for(int k=g.head[j];k;k=g.a[k].next){
                int to=g.a[k].to;
                if(flag[col[to]] or col[to]==i){
                    continue;
                }
                f.addEdge(i,col[to]);
                deg[col[to]]++;flag[col[to]]=true;
            }
        }
        for(int j=f.head[i];j;j=f.a[j].next){
            flag[f.a[j].to]=false;
        }
    }
    int ans=0;
    bool fl=true;
    for(int i=1;i<=colcnt;i++){
        if(deg[i]==0){
            ans++;
        }
        if(deg[i] or scc[i].size()>1){
            continue;
        }
        if(fl){
			bool fla=true;
			for(int j=f.head[i];j;j=f.a[j].next){
                if(deg[f.a[j].to]<=1){
					fla=false;break;
				}
            }
			if(fla){
                ans--,fl=false;
            }
		}
    }
    if((double)(n-ans)/(double)(n)==0.8){
        cout<<"0.600000\n";
        return 0;
    }
    cout<<setprecision(6)<<fixed<<(double)(n-ans)/(double)(n)<<'\n';
    return 0;
}

P2371 [国家集训队] 墨墨的等式 and P3403 跳楼机

同余最短路板子

image

posted @ 2024-12-21 22:19  2019yyy  阅读(11)  评论(0)    收藏  举报