来解剖 来平息你的颤抖 叫嚷着还不足够 还需要更多疼痛 才值得温柔

test4

不要在意这个诡异的标题。


排序sort

快排的过程相当于以 \(a_r\) 为界限,更小的放到左边,更大的放在右边,我们还关心新的 \(a_r\) 是谁,左边是按顺序的填入,右边新的顺序只跟原本的顺序有关系素排列双射下去啦,所以就是唯一特定位置的值成为新的。

那么考虑 dp 一下 \(f[i][j]\) 表示长度为 \(i\) 做了 \(j\) 轮的方案数,枚举 \(a_r\) 大小 \(l\),先选择左右边的位置 \(\binom{i-1}{l-1}\),然后乘上左右的方案数 \(f[l-1][j-1]\times f[i-l][j-1]\) 即可。

#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pb push_back

using namespace std;

const int N=305;

int T, P, n, k, f[N][N], fac[N][N];

signed main() {
	freopen("sort.in","r",stdin);
	freopen("sort.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> T >> P, n=300;
	up(i,0,n) {
		fac[i][0]=fac[i][i]=1;
		up(j,1,i-1) fac[i][j]=(fac[i-1][j-1]+fac[i-1][j])%P;
	}
	up(i,0,n) f[i][0]=1;
	up(j,1,n) {
		f[0][j]=1;
		up(i,1,n) up(l,1,i) {
			(f[i][j]+=f[l-1][j-1]*f[i-l][j-1]%P*fac[i-1][l-1]%P)%=P;
		}
	}
	while(T--) {
		cin >> n >> k;
		cout << f[n][k-1] << '\n';
	}
	return 0;
}

染色col

\(a=b\) 显然是 \(2(n-1)-\max\{dis_i\}\),十分经典。

\(a\neq b\),要让 B 跟着 A 走,首先到中点会合最快,其次没必要为了最远点贡献走远(会 \(+1-1\)),算出 B 开始贡献的步数和点然后当 \(a=b\) 的问题再补全贡献即可。

#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pb push_back

using namespace std;

const int N=200005;

int T, n, a, b, Ans, dep[N], len, fa[N];
vector<int> to[N]; 

void dfs(int x) {
	for(int y:to[x]) if(!dep[y]) {
		dep[y]=dep[x]+1;
		fa[y]=x, dfs(y);
		len=max(len,dep[y]);
	}
}

void sol(int S) {
	up(i,1,n) dep[i]=0;
	len=1, dep[S]=1, dfs(S);
}

int upper(int x,int d) {
	if(dep[x]==d) return x;
	return upper(fa[x],d);
}

void mian() {
	Ans=0;
	cin >> n >> a >> b;
	up(i,1,n) to[i].clear();
	up(i,2,n) {
		int u, v;
		cin >> u >> v;
		to[u].pb(v);
		to[v].pb(u);
	}
	sol(a);
	if(a==b) {
		cout << 2*(n-1)-len+1 << '\n';
		return; 
	}
	if(dep[b]&1) {
		Ans+=(dep[b]-1)/2;
		int c=upper(b,(1+dep[b])/2);
		sol(c), Ans+=2*(n-1)-len+1;
	}
	else {
		Ans+=dep[b]/2;
		int c=upper(b,dep[b]/2);
		sol(c), Ans+=2*(n-1)-len+1;
	}
	cout << Ans << '\n';
}

signed main() {
	freopen("col.in","r",stdin);
	freopen("col.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> T;
	while(T--) mian();
	return 0;
}

序列seq

显然等价与排序后对 \(2\to n\) 进行划分使得差分都单调不降。

考虑 \(?\to i\),可以将点分成三类。

  • 对于 \(i-1\to i\),只关心另一条的末尾 \(pos\) 和最大合法 \(pre_{pos}\)

    维护一个序列。

  • 对于 \(i-2\to i\),只关心最大的 \(pre_{i-1}\)

    维护一个点。

  • 对于 \(x(<i-2)\to i\),只关心最大的 \(pre_i\)

    还是维护一个点。

转移是简单的,向后做一下就行了,注意顺序。

#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pii pair<int,int>
#define mp make_pair

using namespace std;

const int N=300005, inf=1e13;

int T, n, a[N], b[N], c[N], m, pos[N], val[N];

bool check(int x,int y,int z) { return a[y]-a[x]<=a[z]-a[y]; }

void insr(int x,int v) {
	if(m&&x==pos[m]) {
		val[m]=min(val[m],v);
		return;
	}
	while(m&&val[m]<=v) --m;
	++m, pos[m]=x, val[m]=v;
}

void mian() {
	m=0;
	cin >> n;
	up(i,1,n) cin >> a[i];
	sort(a+1,a+1+n);
	int can=1;
	up(i,3,n-1) {
		b[i+1]=c[i+1]=0;
		if(can) {
			if(i==3) b[i]=max(b[i],1ll);
			else c[i]=max(c[i],1ll);
		}
		if(!check(i-2,i-1,i)) can=0;
		int u=upper_bound(val+1,val+1+m,a[i+1])-val-1;
		if(1<=u&&u<=m) c[i+1]=max(c[i+1],pos[u]);
		if(!check(i-1,i,i+1)) m=0;
		if(b[i]) {
			if(check(i-2,i,i+1)) insr(i-1,2*a[i-1]-a[b[i]]);
			if(check(b[i],i-1,i+1)) b[i+1]=max(b[i+1],i-2);
		}
		if(c[i]) {
			if(check(c[i],i,i+1)) insr(i-1,2*a[i-1]-a[i-2]);
			if(check(i-2,i-1,i+1)) b[i+1]=max(b[i+1],c[i]);
		}
	}
	if(can||m||b[n]||c[n]) cout << "YES\n";
	else cout << "NO\n";
}

signed main() {
//	freopen("1.txt","r",stdin);
	freopen("seq.in","r",stdin);
	freopen("seq.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> T;
	while(T--) mian();
	return 0;
}

首先忘了——

然后博弈论点分树放弃了不写了。

posted @ 2025-09-15 21:53  Hypoxia571  阅读(6)  评论(0)    收藏  举报