「APIO2018」铁人两项 题解

先考虑部分分...

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

期望得分 \(\rm 16 \ pts\)

\(\rm 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 \rightarrow c \rightarrow f\)\(f \rightarrow c \rightarrow s\) 算两种不同的方案,所以最后答案还要乘个 \(2\)

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

\(\rm 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\) 的系数就行。

期望得分 \(\rm39+35=74 \ pts\)


至于正解...直接对原图建广义圆方树,然后做法同 \(\rm 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;
}

posted on 2020-07-16 20:30  exzang  阅读(104)  评论(1)    收藏  举报

导航