来解剖 来平息你的颤抖 叫嚷着还不足够 还需要更多疼痛 才值得温柔
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;
}
首先忘了——
然后博弈论点分树放弃了不写了。