zoukankan      html  css  js  c++  java
  • 【APIO2018】铁人两项

    【APIO2018】铁人两项

    题目描述

    大意就是给定一张无向图,询问三元组((s,c,f))中满足(s eq c eq f)且存在((s o c o f))的简单路径(每个点最多经过一次)的数量。

    (1leq n,leq 10^5,1leq mleq 2*10^5)

    我们考虑枚举(s,f)然后计算中间(c)的数量。我们发现对于一张图上统计两点之间路径上的点数量很好做。于是我们考虑建圆方树。

    我们将圆点的权值定为(-1),将方点的权值定为与其直接相连的圆点的数量。(u)(v)路径上可能经过的点的数量就是圆方树上(u o v)路径上除了(u,v)的点权之和(-2)

    于是我们用树形( ext{DP}),计算每个点对答案的贡献。

    代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define N 200005
    
    using namespace std;
    inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
    
    int n,m;
    int tot;
    struct graph {
    	int to[N<<2],nxt[N<<2];
    	int h[N],cnt;
    	void add(int i,int j) {
    		to[++cnt]=j;
    		nxt[cnt]=h[i];
    		h[i]=cnt;
    	}
    }s,g;
    
    int dfn[N],low[N],id;
    int st[N];
    int val[N];
    
    void work(int v,int to) {
    	tot++;
    	g.add(v,tot);
    	val[tot]=1;
    	while(1) {
    		int j=st[st[0]--];
    		val[tot]++;
    		g.add(tot,j);
    		if(j==to) return ;
    	}
    }
    
    ll tot_size;
    void tarjan(int v,int fr) {
    	tot_size++;
    	dfn[v]=low[v]=++id;
    	st[++st[0]]=v;
    	for(int i=s.h[v];i;i=s.nxt[i]) {
    		int to=s.to[i];
    		if(to==fr) continue ;
    		if(!dfn[to]) {
    			tarjan(to,v);
    			low[v]=min(low[v],low[to]);
    			if(low[to]>=dfn[v]) {
    				work(v,to);
    			}
    		} else low[v]=min(low[v],dfn[to]);
    	}
    }
    
    ll ans;
    int size[N];
    void dfs(int v) {
    	int tim=0;
    	for(int i=g.h[v];i;i=g.nxt[i]) {
    		int to=g.to[i];
    		dfs(to);
    		ans+=1ll*val[v]*size[v]*size[to];
    		tim+=size[v]*size[to];
    		size[v]+=size[to];
    	}
    	if(v<=n) size[v]++;
    	if(v<=n) {
    		tim+=(size[v]-1)*(tot_size-size[v]);
    		ans+=1ll*val[v]*(size[v]-1)*(tot_size-size[v]);
    	} else {
    		tim+=size[v]*(tot_size-size[v]);
    		ans+=1ll*val[v]*size[v]*(tot_size-size[v]);
    	}
    }
    
    int main() {
    	n=Get(),m=Get();
    	tot=n;
    	int a,b;
    	for(int i=1;i<=m;i++) {
    		a=Get(),b=Get();
    		s.add(a,b),s.add(b,a);
    	}
    	for(int i=1;i<=n;i++) val[i]=-1;
    	for(int i=1;i<=n;i++) {
    		if(dfn[i]) continue ;
    		tot_size=0;
    		tarjan(i,0);
    		dfs(i);
    		ans-=1ll*tot_size*(tot_size-1);
    	}
    	cout<<ans*2;
    	return 0;
    }
    
    
  • 相关阅读:
    加密和解密配置节(asp2.0以后)http://www.cnitblog.com/lby91772/archive/2008/03/04/40436.html
    多线程消息队列 (MSMQ) 触发器 http://blog.sina.com.cn/s/blog_405ad00201007rlw.html
    NUnit的使用
    HDU 1896 Stones
    POJ 1862 Stripies(哈夫曼)
    ZOJ 3230 Solving the Problems
    HDU 1242 Rescue(BFS)
    ZOJ 3410 Layton's Escape
    求逆序数
    HDU 1873 看病要排队
  • 原文地址:https://www.cnblogs.com/hchhch233/p/10574714.html
Copyright © 2011-2022 走看看