AtCoder Regular Contest 186
佬:你的水平什么时候也配打arc了?
我:被队友抓来补题的一生
B
伪装计数的建图题
用到了一点继承的思想吧,看起来是差分约束但是最后发现是一个树
这个时候涉及到两种建树方案
1.从每个点a[i]往i连边,建成一个以0为根的树,此时子节点之间有大小关系(这个建树很好写)
2.把子节点之间的大小关系也拆开建成一棵二叉树,左儿子为原本的最小的儿子,右儿子为比他大的兄弟节点(这个可以递归的去建树)
之前没想明白建树方案导致不会统计答案
第一种统计答案可以直接看代码,第二种我不会建树,统计答案非常简单宝宝都会
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define pii pair<int,int>
#define mkp make_pair
#define pb push_back
const int maxn=4e5+10,mod=998244353;
int n;
int a[maxn],siz[maxn],ans=0,fact[maxn],finv[maxn];
bool vis[maxn];
vector<int>E[maxn];
int add(int x,int y){
return (x+y>mod)?(x+y-mod):(x+y);
}
int sub(int x,int y){
return (x-y<0)?(x-y+mod):(x-y);
}
int qpow(int x,int y){
int res=1;
while(y){
if(y&1)res=res*x%mod;
x=x*x%mod;y>>=1;
}
return res;
}
int comb(ll n,ll r){ if(n<r||r<0)return 1; return fact[n]*finv[n-r]%mod*finv[r]%mod;}
void solve(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
fact[0]=finv[0]=1;
for(int i=1;i<=n+1;i++)fact[i]=fact[i-1]*i%mod;
finv[n+1]=qpow(fact[n+1],mod-2);
for(int i=n;i>=1;i--)finv[i]=finv[i+1]*(i+1)%mod;
for(int i=0;i<=n;i++)siz[i]=1;
for(int i=n;i>=1;i--){
E[a[i]].push_back(i);
siz[a[i]]+=siz[i];
}
int re=1;
for(int i=0;i<n;i++){
auto rem=siz[i]-1;
for(auto u:E[i]){
re=re*comb(rem-1, siz[u]-1)%mod;
rem=sub(rem,siz[u]);
}
}
cout<<re<<endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int tt=1;while(tt--)solve();
}
C
博弈论部分还是可以观察推理的,就是至少买m个箱子,其中m-1个最大的箱子只会装一个球,剩下的可以装满。
一开始想的是按照v-p的值排序去做,但是这样无法确定m-1个箱子的位置。
考虑用分类的思想把m-1的部分和剩下买的x个分开,那么就可以按v排序,枚举隔断点去做,隔断点前只要v-p大于零就取,隔断点之后取p最小的m-1个