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个

posted @ 2024-10-30 10:05  lyrrr  阅读(47)  评论(0编辑  收藏  举报