省选模拟6

A. 中心城镇问题

\(f_{i,j}\) 表示以 \(i\) 为根的子树内里 \(i\) 最近的选择点的深度为 \(j\)

转移的话

\((j-dep_x)*2>K\) 直接加上对应的位置

\(0 < (j-dep_x)*2\leq K\) 需要判断保证选择的子树之间的距离要大于 \(K\)

还有一种 \(f_i,dep_i=f_i,dep_{i+k+1}+w_i\) 选择自己的情况

是以下标为深度的转移,于是长链剖分优化一下就行

还有保证是后缀最大值

Code
#include<bits/stdc++.h>
#define int long long
#define rint signed
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,k;
int w[1000010],son[1000010],dep[1000010],md[1000010];
int head[1000010],ver[2000010],to[2000010],tot;
vector<int>f[1000010],vec;
inline void add(int x,int y){ver[++tot]=y;to[tot]=head[x];head[x]=tot;}
void dfs1(int x,int fa){
	md[x]=dep[x]=dep[fa]+1;
	for(int i=head[x];i;i=to[i]){
		int y=ver[i];
		if(y==fa) continue;
		dfs1(y,x);
		md[x]=max(md[x],md[y]);
		if(md[y]==md[x]) son[x]=y;
	}
}
void dfs2(int x,int fa){
	if(!son[x]) return f[x].emplace_back(w[x]),void();
	dfs2(son[x],x);swap(f[x],f[son[x]]);
	if(f[x].size()>k){
		f[x].emplace_back(max(f[x][f[x].size()-1-k]+w[x],f[x].back()));
	}else f[x].emplace_back(max(w[x],f[x].size()?f[x].back():0ll));
	for(int i=head[x],s,t,e;i;i=to[i]){
		int y=ver[i];
		if(y==fa||y==son[x]) continue;
		dfs2(y,x);e=f[y].size()-1;
		t=f[x].size()-1;s=t-f[y].size();
		for(int i=s;i<=t;i++) vec[i]=0;
		for(int i=s,d;i<=t;i++){
			d=dep[x]+t-i;vec[i]=f[x][i];
			if(2*(d-dep[x])>k){
				vec[i]+=f[y][f[y].size()-1-(d-dep[y])];
			}else if(2*(d-dep[x])>0){
				if(e-(k+1-d+2*dep[x]-dep[y])>=0) vec[i]=max(vec[i],f[x][i]+f[y][e-(k+1-d+2*dep[x]-dep[y])]);
				if(t-(k+1-d+2*dep[x]-dep[x])>=0&&d!=(k+1-d+2*dep[x])) vec[i]=max(vec[i],f[x][t-(k+1-d+2*dep[x]-dep[x])]+f[y][e-(d-dep[y])]);
			}else if(md[y]>dep[x]+k){
				vec[i]=max(vec[i],f[x][t]+f[y][e-(dep[x]+1+k-dep[y])]);
			}
			if(i>s) vec[i]=max(vec[i],vec[i-1]);
			if(d-dep[y]>=0) vec[i]=max(vec[i],f[y][e-(d-dep[y])]);
		}
		for(int i=s;i<=t;i++) f[x][i]=vec[i];
		f[y].clear();f[y].shrink_to_fit();
	}
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	freopen("central.in","r",stdin);
	freopen("central.out","w",stdout);
	n=read(),k=read();vec.resize(1000010);
	for(int i=1;i<=n;i++) w[i]=read();
	for(int i=1,x,y;i<n;i++){
		x=read(),y=read();
		add(x,y),add(y,x);
	}
	dfs1(1,0);dfs2(1,0);
	printf("%lld\n",f[1].back());
	return 0;
}

B. 心理阴影

一个点对产生贡献时,只跟分别到 \(lca\) 处的路径上的点有关,跟其他点无关

于是考虑在 \(lca\) 处合并子树之间的答案

\(siz_u\) 表示 \(u\) 的子树大小, \(g(u,v)\) 表示 \(v\) 子树内比 \(u\) 小的数的个数

\(f_{u,v}\) 表示将 \(u,v\) 子树合并时产生的逆序对的平均数

分别是先选 \(u\) 的情况和先选 \(v\) 的情况

先选了 \(u\) 那么要加上 \(u\) 的每一个子树和 \(v\) 去合并的贡献,还有 \(g(u,v)\)

由于每个子树之间互补影响与 \(v\) 的贡献,所以可以直接加

先选 \(v\) 的同理

还需要乘上一个系数

最后就是 \(f(u,v)=\frac{siz_u*(\sum\limits_{w}f(w,v)+g(u,v))+siz_v*(\sum\limits_w f(w,u)+g(v,u))}{siz_u+siz_v}\)

还有一种情况,就是为祖先关系时,无论如何都会产生贡献要单独加上

Code
#include<bits/stdc++.h>
//#define int long long
#define rint signed
#define mod 1000000007
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,rt,ans;
int inv[5000],siz[5000],fa[5000];
int head[5000],ver[10000],to[10000],tot;
int f[5000][5000],g[5000][5000];
inline void add(int x,int y){ver[++tot]=y;to[tot]=head[x];head[x]=tot;}
inline void md(int &x){x=(x>=mod)?x-mod:x;}
inline int qpow(int x,int k){
	int res=1,base=x;
	while(k){if(k&1) res=1ll*res*base%mod;base=1ll*base*base%mod;k>>=1;}
	return res;
}
void dfs(int x,int fath){
	fa[x]=fath;siz[x]=1;for(int i=x+1;i<=n;i++) g[i][x]++;
	for(int i=head[x];i;i=to[i]){
		int y=ver[i];if(y==fath) continue;
		dfs(y,x);siz[x]+=siz[y];
		for(int i=1;i<=n;i++) g[i][x]+=g[i][y];
	}
}
inline int calc(int x,int y){
	if(~f[x][y]) return f[x][y];
	md(f[x][y]=(1ll*siz[x]*g[x][y]%mod+1ll*siz[y]*g[y][x]%mod));
	for(int i=head[x];i;i=to[i]){
		int z=ver[i];if(z==fa[x]) continue;
		md(f[x][y]+=1ll*siz[x]*calc(z,y)%mod);
	}
	for(int i=head[y];i;i=to[i]){
		int z=ver[i];if(z==fa[y]) continue;
		md(f[x][y]+=1ll*siz[y]*calc(z,x)%mod);
	}
	f[x][y]=1ll*f[x][y]*inv[siz[x]+siz[y]]%mod;
	return f[y][x]=f[x][y];
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	freopen("nightmare.in","r",stdin);
	freopen("nightmare.out","w",stdout);
	for(int i=1;i<=4096;i++) inv[i]=qpow(i,mod-2);
	n=read(),rt=read();
	for(int i=1,x,y;i<n;i++){x=read(),y=read();add(x,y),add(y,x);}
	dfs(rt,0);memset(f,-1,sizeof(f));
	for(int i=1;i<=n;i++) ans+=g[i][i];
	for(int i=1;i<=n;i++){
		for(int j=head[i];j;j=to[j]){
			int x=ver[j];if(x==fa[i]) continue;
			for(int k=head[i];k;k=to[k]){
				int y=ver[k];if(y==fa[i]) continue;if(x>=y) continue;
				md(ans=(ans+calc(x,y)));
			}
		}
	}
	printf("%d\n",ans);
	return 0;
}

C. Pajel 游戏

手模前 \(4\) 个,第 \(5\) 个并查集缩一下再染色就行

后面的右手扶墙走,碰到一个不一样的颜色就换一种颜色染,最后再手动调整

posted @ 2022-01-21 20:43  Max_QAQ  阅读(66)  评论(0编辑  收藏  举报