AGC/ARC 难题集

AGC024E Sequence Growing Hard


ARC097D Monochrome Cat

开始想的是一个暴力的换根 dp 模拟题意的选择,打到一半意识到严重的问题就是代码 3K 起步且可能要调一年,所以灰溜溜去看题解了。
题解发现性质就能转化成简单问题的 dp。首先不难发现如果一个无根树的子树(意会一下)全黑就肯定不需要动它,所以从黑叶子开始 bfs 把这些坨坨都删掉,变成一棵新树。显然行走方案是:从 \(u\) 出发,走到每一个叶子,所有叶子都走到之后有两种选择,要么返回 \(u\),要么在返回 \(u\) 的路上某个点停下,如果最后一些经过的点没变成黑,就用额外使用操作 2 变成黑。于是没有走的一部分一定是一条到 \(u\) 结束的树上路径。考虑先把假设返回 \(u\) 需要多少代价算出来,再减去路径的最大返收益。由于前者其实和 \(u\) 无关,我们把它算出来,叫做 \(sum\),剩下的问题便是从树上选一条返收益最大的有向路径,其中返收益可以表示出来:
\(s\to t [len]\) 的返收益:\(len+\) 路径(except \(s\))上额外反转的点-路径(except \(s\))上未额外反转的点
至此可以使用树形 dp 解决了。

/*
dp: let f[i] denote the maximum refund for directional routes in i's subtree.
f[i][2]=max(f[i][2],f[i][1]+f[son][0]+1+ew[son]-!ew[son],f[i][0]+f[son][1]+1+ew[i]-!ew[i])
f[i][0]=max(f[i][0],f[son][0]+1+ew[son]-!ew[son])
f[i][1]=max(f[i][1],f[son][1]+1+ew[i]-!ew[i])
*/
#include <bits/stdc++.h>
using namespace std;
inline int read(){
	register char ch=getchar();register int x=0;
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x;
}
const int N=1e5+5;
int n,l=1,r,rt,sum,Edge,u[N],v[N],deg[N],col[N],f[N][3],Q[N];
bool del[N],ew[N];
char s[N];
vector<int>G[N];
void shan(){
	for(int i=1;i<=n;i++)if(deg[i]==1&&col[i])Q[++r]=i,del[i]=1;
	while(l<=r){
		int x=Q[l++];
		for(int y:G[x])if(!del[y]){
			if((--deg[y])==1&&col[y])Q[++r]=y,del[y]=1;
		}
	}
	for(int i=1;i<=n;i++)G[i].clear();
	for(int i=1;i<n;i++)if(!del[u[i]]&&!del[v[i]])
		G[u[i]].push_back(v[i]),G[v[i]].push_back(u[i]),Edge++;
	for(int i=1;i<=n;i++)if(!del[i])rt=i;
	if(!rt){puts("0");exit(0);}
}
void dfs(int x,int p){
	for(int y:G[x])if(y^p){
		dfs(y,x);
		f[x][2]=max(f[x][2],max(f[x][1]+f[y][0]+1+ew[y]-!ew[y],f[x][0]+f[y][1]+1+ew[x]-!ew[x]));
		f[x][0]=max(f[x][0],f[y][0]+1+ew[y]-!ew[y]);
		f[x][1]=max(f[x][1],f[y][1]+1+ew[x]-!ew[x]);
	}
}
int main(){
	n=read();
	for(int i=1;i<n;i++){
		u[i]=read(),v[i]=read();
		G[u[i]].push_back(v[i]),G[v[i]].push_back(u[i]);
		deg[u[i]]++,deg[v[i]]++;
	}
	scanf("%s",s+1);
	for(int i=1;i<=n;i++)col[i]=s[i]=='B';
	shan();
	sum=2*Edge;
	for(int i=1;i<=n;i++)if(!del[i]&&!((deg[i]&1)^col[i]))ew[i]=1,sum++;
	dfs(rt,0);
	int mx=0;
	for(int i=1;i<=n;i++)if(!del[i])mx=max(mx,max(f[i][2],max(f[i][1],f[i][0])));
	cout<<sum-mx<<'\n';
}

AGC020F Arcs on a Circle

【套路】在实数域内的期望位置关系问题,通常满足:题目中的关系只与各元素所在位置的整数部分,以及小数部分的相对大小有关。因此全排列各元素小数部分的相对大小,将每个 1 单位长度作出 n 等分点,从而把取数变成取点,可以进一步解决;由随机性保证正确。

线段覆盖 DP 的一种典型状态设计:设 \(f(s,i,j)\) 表示已经用了 \(s\) 这些线段,起点在前 \(i\) 个点之中,最远已经完整覆盖到第 \(j\) 个点, 的概率。

本题的特殊性:在环上。解决方法:断环为链。断环为链有讲究,如果我们任选一条线段的起点定为断点,则可能出现下图的情况,就还需要考虑那种从尾部又穿到头部来补救的情形。

但是如果以 最长 的线段的起点作为起头,那么处于尾部的线段即使穿到首部也不会伸到比第一条线段的结尾更长的地方,因此可以忽略。

#include <bits/stdc++.h>
using namespace std;
int n,c,l[8],p[8];
double ans,f[305][305][1<<6],sf[305][305][1<<6];
int main(){
	cin>>n>>c;
	for(int i=1;i<=n;i++)cin>>l[i];
	sort(l+1,l+n+1,greater<int>());
	int jc=1;
	for(int i=1;i<n;i++)jc*=i;
	for(int i=1;i<=n;i++)p[i]=i;
	do {
		f[1][l[1]*n+1][1]=1;
		for(int j=1;j<=n*c+1;j++)sf[1][j][1]=sf[1][j-1][1]+f[1][j][1];
		for(int i=2;i<=n*c;i++){
			for(int s=1;s<(1<<n);s+=2){
				for(int j=i;j<=n*c+1;j++){
					int tmp=p[(i-1)%n+1]-1;
					f[i][j][s]=f[i-1][j][s];
					if(s>>tmp&1){
						if(min(n*c+1,l[tmp+1]*n+i)==j){
							f[i][j][s]+=(sf[i-1][j][s^(1<<tmp)]-sf[i-1][i-1][s^(1<<tmp)])/c;
						}
						else if(min(n*c+1,l[tmp+1]*n+i)<j)
							f[i][j][s]+=f[i-1][j][s^(1<<tmp)]/c;
					}
				}
				for(int j=1;j<=n*c+1;j++)sf[i][j][s]=sf[i][j-1][s]+f[i][j][s];
			}
		}
		ans+=f[n*c][n*c+1][(1<<n)-1];
	}
	while(next_permutation(p+2,p+n+1));
	printf("%.13f",ans/jc);
}
posted @ 2022-07-24 11:20  pengyule  阅读(56)  评论(0编辑  收藏  举报