• 「APIO2018」铁人两项 题解


    先考虑部分分...

    ( m Subtask 1 & 2):直接 (mathcal{O(n^4)}) 暴力做即可。

    期望得分 ( m 16 pts)

    ( m Subtask 4 & 5):非环的情况,可以考虑树形 dp 去做。

    (f_i) 为以 (i) 为中转点的点对数量,然后式子很显然...直接看代码如下:

    	for(int i=0;i<g[x].size();++i){
    		int v=g[x][i];
    		if(v==fath) continue;
    		dfs(v,x);
    		ans+=1ll*sz[x]*sz[v];
    		sz[x]+=sz[v];	
    	}
    	++sz[x];
    	ans+=1ll*(all-sz[x])*(sz[x]-1);
    

    由于图并不保证联通,所以用 all 来表示联通块的点的数量。

    顺便一提由于 (s ightarrow c ightarrow f)(f ightarrow c ightarrow s) 算两种不同的方案,所以最后答案还要乘个 (2)

    期望得分 ( m 16+23=39 pts)

    ( m Subtask 6 & 7):仙人掌的情况...仙人掌+dp 那不就是在圆方树上 dp 吗...

    跟上面一样设 (f_i) 为以 (i) 为中转点的点对数量,圆点的情况和原来差不多。

    对于方点的情况...分类讨论一下。

    (Bsz) 为方点代表的环的大小。

    (s)(f) 都在方点代表的环内:显然中转点有 (Bsz-2)

    (s)(f) 两个都不在方点代表的环内:有 (Bsz) 个中转点。然而会被 (2) 个圆点给计算过一次,发现会有重复,去重一下就有 (Bsz-2) 个中转点。

    (s)(f) 有且只有一个在方点代表的环内:(Bsz-1) 个中转点。发现会算重,去重一下 (Bsz-2) 个中转点。

    综上所述中转点数量有 (Bsz-2) 个。于是在算的时候乘上一个 (Bsz-2) 的系数就行。

    期望得分 ( m39+35=74 pts)


    至于正解...直接对原图建广义圆方树,然后做法同 ( m Subtask 6 & 7)

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=200000+5;
    struct edge{
    	int nxt,to;
    }e[N<<1];
    int head[N],cnt=1;
    vector<int> g[N];
    void add(int x,int y){
    	e[++cnt].to=y; e[cnt].nxt=head[x]; head[x]=cnt;
    }
    int n,all,dfn[N],low[N],tot,sz[N],Bsz[N],amt;
    stack<int> S;
    void tarjan(int x,int fath){
    	dfn[x]=low[x]=++tot;
    	S.push(x);
    	++all;
    	for(int i=head[x];i;i=e[i].nxt)
    		if(e[i].to!=fath){
    			int v=e[i].to;
    			if(!dfn[v]){
    				tarjan(v,x);
    				if(low[v]==dfn[x]){
    					++amt;
    					g[amt].push_back(x);
    					g[x].push_back(amt);
    					Bsz[amt]=1;
    					while(1){
    						int now=S.top();S.pop();
    						g[now].push_back(amt);
    						g[amt].push_back(now);
    						Bsz[amt]++;
    						if(now==v) break;
    					}
    				}
    				else if(low[v]>dfn[x]) S.pop(),g[x].push_back(v),g[v].push_back(x);
    				low[x]=min(low[x],low[v]);
    			}
    			else low[x]=min(low[x],dfn[v]);
    		}
    }
    long long ans;
    void dfs(int x,int fath){
    	for(int i=0;i<g[x].size();++i){
    		int v=g[x][i];
    		if(v==fath) continue;
    		dfs(v,x);
    		if(x<=n) ans+=1ll*sz[x]*sz[v];
    		else ans+=1ll*(Bsz[x]-2)*sz[x]*sz[v];
    		sz[x]+=sz[v];	
    	}
    	if(x<=n){
    		++sz[x];
    		ans+=1ll*(all-sz[x])*(sz[x]-1);
    	}
    	else ans+=1ll*(Bsz[x]-2)*(all-sz[x])*sz[x];
    }
    int main(){
    	int m;
    	cin>>n>>m;
    	int U,V;
    	for(int i=1;i<=m;++i){
    		cin>>U>>V;
    		add(U,V);add(V,U);
    	}
    	amt=n;
    	for(int i=1;i<=n;++i)
    		if(!dfn[i]){
    			all=0;
    			tarjan(i,0);
    			dfs(i,0);
    		}
    	cout<<ans*2ll;
    	return 0;
    }
    
    
  • 相关阅读:
    Asp.Net MVC ajax调用 .net 类库问题
    sql关键查询
    js数组
    win2003 sp2+iis 6.0上部署.net 2.0和.net 4.0网站的方法
    Linux开启服务器问题(李蕾问题)
    SWFUpload 中文乱码问题
    设置html滚动条(陶庭飞问题)
    反射(前台到后台,后台到前台)
    MySql数据类型
    将页面居中
  • 原文地址:https://www.cnblogs.com/limit-ak-ioi/p/13324877.html
Copyright © 2020-2023  润新知