【题解】AT2064 Many Easy Problems(转换+NTT)

【题解】AT2064 Many Easy Problems(转换+NTT)

给定一棵树,请你回答\(k\in[1,n]\)由\(k\)个点生成出来的虚树(steiner)的所有方案的大小的和。

对于这种分元素然后每个元素对答案有一个相同的贡献的计数,一般都是考虑对于一个点考虑对于答案的贡献。对于一个确定的\(k\)和一个点\(p\),可以很轻易的算出\(p\)对于答案的贡献=\({n\choose k }-( \sum_{u \in Son}siz[u])-(n-siz[p])\)。我们拿个同记录相同的组合数的上面的那个数,这个数组设为\(s_i\)那么设答案为\(a_k\),有(\(b_0=0\))
\[
a_k=\sum_{i=0}^n b_i{i\choose k}
\]
拆开
\[
a_k=\sum_{i=0} {b_i i!\over k! (i-k)!}
\]
随便化一下
\[
k!a_k=\sum_{i} {(b_ii!)\over (i-k)!}
\]
按照上次的那种套路搞出来就能NTT了

这一发TLE了,不知道为什么......

upd: AtCoder 编译命令没有-DONLINE_JUDGE

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;  typedef long long ll;   char __buf[1<<18],*__c=__buf,*__ed=__buf;
inline int qr(){
    int ret=0,f=0,c=getchar();
    while(!isdigit(c))f|=c==45,c=getchar();
    while(isdigit(c)) ret=ret*10+c-48,c=getchar();
    return f?-ret:ret;
}
const int maxn=1<<19|1;
const int mod=998244353;
const int g=3;
const int gi=(mod+1)/3;
typedef vector<int> poly;
poly buk(maxn),c(maxn);
int inv[maxn],siz[maxn],r[maxn],jc[maxn],n;

inline int MOD(const int&x){return x-mod>=0?x-mod:x;}
inline int MOD(const int&x,const int&y){return 1ll*x*y%mod;}
inline int MOD(const vector<int>&ve){int ret=1;for(const auto&t:ve) ret=MOD(ret,t); return ret;}

inline int ksm(const int&ba,const int&p){
    int ret=1;
    for(int t=p,b=ba;t;t>>=1,b=MOD(b,b))
        if(t&1) ret=MOD(ret,b);
    return ret;
}

void pre(const int&n){
    jc[0]=inv[0]=1;
    for(int t=1;t<=n;++t) jc[t]=MOD(jc[t-1],t);
    inv[n]=ksm(jc[n],mod-2);
    for(int t=n-1;t;--t) inv[t]=MOD(inv[t+1],t+1);
    for(int t=1;t<=n;++t)
        if(MOD(jc[t],inv[t])!=1)
            puts("wa");
}

void NTT(poly&a,const int&tag){
    static int r[maxn];
    int len=a.size();
    for(int t=1;t<len;++t)
        if((r[t]=r[t>>1]>>1|(t&1?len>>1:0))>t)
            swap(a[t],a[r[t]]);
    for(int t=1,wn,s=tag==1?g:gi;t<len;t<<=1){
        wn=ksm(s,(mod-1)/(t<<1));
        for(int i=0;i<len;i+=t<<1)
            for(int j=0,w=1,p;j<t;++j,w=MOD(w,wn))
                p=MOD(a[i+j+t],w),a[i+j+t]=MOD(a[i+j]-p+mod),a[i+j]=MOD(a[i+j]+p);
    }
    if(tag!=1)
        for(int t=0,i=mod-(mod-1)/len;t<len;++t)
            a[t]=MOD(a[t],i);
}

poly operator * (poly a,poly b){
    int t1=a.size()+b.size()-1,len=1;
    while(len<t1) len<<=1;
    a.resize(len); b.resize(len);
    NTT(a,1); NTT(b,1);
    for(int t=0;t<len;++t) a[t]=MOD(a[t],b[t]);
    NTT(a,-1); a.resize(t1);
    return a;
}

poly e[maxn];
void add(int fr,int to){
    e[fr].push_back(to);
    e[to].push_back(fr);
}

void dfs(int now,int last){
    siz[now]=1;
    for(auto t:e[now])
        if(t^last)
            dfs(t,now),++buk[siz[t]],siz[now]+=siz[t];
    if(n-siz[now]>=0) ++buk[n-siz[now]];
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("in.in","r",stdin);
    //freopen("out.out","w",stdout);
#endif
    n=qr();
    buk.resize(n+1);
    c.resize(n+1);
    for(int t=1;t<n;++t) add(qr(),qr());
    dfs(1,0); pre(2e5);
    for(int t=0;t<=n;++t) buk[t]=MOD(buk[t],jc[t]);
    reverse(buk.begin(),buk.end());
    for(int t=0;t<=n;++t) c[t]=inv[t];
    poly ans=buk*c;
    ans.resize(n+1);
    reverse(ans.begin(),ans.end());
    for(int t=1;t<=n;++t) printf("%d\n",MOD(MOD({jc[n],inv[t],inv[n-t],n})-MOD(ans[t],inv[t])+mod));
    return 0;
}

原文地址:https://www.cnblogs.com/winlere/p/12181215.html

时间: 2024-10-31 23:19:37

【题解】AT2064 Many Easy Problems(转换+NTT)的相关文章

[题解] [AGC005F] Many Easy Problems

题面 题解 学长讲课题目的质量果然和我平常找的那些不一样 思路还是可以说比较巧妙的 考虑到我们并不好算出对于所有大小为 \(i\) 的点集,能够包含它的最小连通块大小 转换题目 这个时候我们应该想到把目标放到单个点 \(i\) 对选择 \(k\) 个点时的贡献 那么他的贡献就是总方案数减去没选的方案数对吧 没选的方案怎么算呢 记 \(sz[i]\) 为以 \(i\) 为根的子树的点数 分析发现, 当 \(k\) 个点同时在以它的儿子为根的一棵子树内就不会计算 \(i\) 这里的儿子的意义是, 以

【AtCoder】AGC005 F - Many Easy Problems 排列组合+NTT

[题目]F - Many Easy Problems [题意]给定n个点的树,定义S为大小为k的点集,则f(S)为最小的包含点集S的连通块大小,求k=1~n时的所有点集f(S)的和取模924844033.n<=2*10^5. [算法]排列组合+NTT [题解]考虑每个点只会在k个点都在其一个子树时无贡献,即: $$ANS_k=\sum_{x=1}^{n}\binom{n}{k}-\sum_{y}\binom{sz[y]}{k}+\binom{n-sz[y]}{k}$$ 令$cnt_i$表示满足s

Codeforces B. Too Easy Problems

题目描述: time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output You are preparing for an exam on scheduling theory. The exam will last for exactly T milliseconds and will consist of n problems. You

AGC005F - Many Easy Problems

题目描述 给定一棵树,设 $f_k$ 表示任选 $k$ 个点组成的最小连通块之和,求 $f(i),i \in [1,n]$ 数据范围 $n \le 2 \times 10^5$ 题解 考虑容斥,即一个点对于 $k$ 不会产生贡献的方案数 于是对于一个点 $u$ ,设其为根,它对 $k$ 的贡献可以列出式子: $(_k^n)-\sum_{v \in son_u}(_k^{size_v})$ 于是 $f_k=n(_k^n)-\sum_{i=1}^ng_i(_k^{i})$ ,其中 $g_i$ 表示子

【题解】An Easy Problem

题目描述 给定一个正整数N,求最小的.比N大的正整数M,使得M与N的二进制表示中有相同数目的1. 举个例子,假如给定的N为78,其二进制表示为1001110,包含4个1,那么最小的比N大的并且二进制表示中只包含4个1的数是83,其二进制是1010011,因此83就是答案. 输入格式 输入若干行,每行一个数n(1≤n≤1000000),输入"0"结束. 输出格式 输出若干行对应的值. 输入样例 1 2 3 4 78 0 输出样例 2 4 5 8 83 题解 容易想到,当$n$加上$low

题解 P1017 【进制转换】

我赶jio这个题难道是让我们写快写? 不管了,赶紧把咕咕咕了一万年的题解写出来. 这个题就是考察负进制和在mod意义下的除法运算的基础运算. (其实也没多大问题) 首先我们先假设一个原始数据\(num\)和基底\(base(1\leq base\leq20)\) 然后不妨设\(num=a*base+b\)(且$b = num\space mod\space a $ ) 重点来了! 如果\(b<0\),我们就\(b-base,a+1\),很明显这样做是正确的. 为啥呢?因为base是负数.负数减去

[题解] Luogu P4245 [模板]任意模数NTT

三模NTT 不会... 都0202年了,还有人写三模NTT啊... 讲一个好写点的做法吧: 首先取一个阀值\(w\),然后把多项式的每个系数写成\(aw + c(c < w)\)的形式,换句话说把多项式\(f(x)\)写成两个多项式相加的形式: \[ f(x) = wf_0(x) + f_1(x) \] 这样在这道题中取\(W = 2^{15}\)就可以避免爆long long了. 乘起来的话就是 \[ f \cdot g = (w f_0 + f_1)(wg_0 + g_1) = (f_0 g

《ACM国际大学生程序设计竞赛题解Ⅰ》——基础编程题

这个专栏开始介绍一些<ACM国际大学生程序设计竞赛题解>上的竞赛题目,读者可以配合zju的在线测评系统提交代码(今天zoj貌似崩了). 其实看书名也能看出来这本书的思路,就是一本题解书,简单暴力的通过题目的堆叠来提升解决编程问题的能力. 那么下面开始探索吧. zoj1037: BackgroundFor years, computer scientists have been trying to find efficient solutions to different computing p

[AGC005]:F - Many Easy Problem

F - Many Easy Problems Time limit : 5sec / Memory limit : 512MB Score : 1900 points Problem Statement One day, Takahashi was given the following problem from Aoki: You are given a tree with N vertices and an integer K. The vertices are numbered 1 thr