P4491 [HAOI2018] 染色

根据模数猜测是 \(NTT\)
\(g_i\) 表示钦定 \(i\) 种颜色有 \(S\) 个,其它不管的方案数,有 \(g_i=C_m^i\frac{n!}{(S!)^i(n-iS)!}(m-i)^{n-iS}\),设 \(f_i\) 表示恰好 \(i\) 种颜色有 \(S\) 个,有 \(g_i=\sum_{j=i}^{m}C_j^if_j\)
根据二项式反演,有 \(f_i=\sum_{j=i}^{\min(n/S,m)}(-1)^{j-i}C_j^ig_j\),把组合数拆开,变成

\[\begin{array}{lr} f_i=\sum_{j=i}^{\min(n/S,m)}\frac{j!}{i!(j-i)!}(-1)^{j-i}g_j\\ f_i=\frac1{i!}\sum_{j=i}^{\min(n/S,m)}(j!g_j)\cdot\frac{(-1)^{j-i}}{(j-i)!} \end{array} \]

\(NTT\) 优化一下即可。

/*
Luogu P3342 [ZJOI2014] 璀灿光华
2026-03-24
*/
#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
const int maxn=100,maxd=343010,maxlight=10,inf=0x3f3f3f3f;
int a,g[maxd],flag[maxd],dis[maxd],cnt_id,ans1=inf,ans2,used[maxd];
int hv[maxlight][10][maxn],cnt_hv[maxlight][10],cnt_fx[maxlight];
bool can[maxd];
string s;
vector<int>e[maxd];
queue<int>q;
void dfs(int u,int h=0){
    if(u==cnt_id+1){ans1=min(ans1,h),ans2=max(ans2,h);return ;}
    for(int i=1;i<=cnt_fx[u];i++){
        int hh=h;
        for(int j=1;j<=cnt_hv[u][i];j++) if(used[hv[u][i][j]]++==0) hh+=g[hv[u][i][j]];
        dfs(u+1,hh);
        for(int j=1;j<=cnt_hv[u][i];j++) used[hv[u][i][j]]--;
    }
    if(cnt_fx[u]!=6) dfs(u+1,h);
}
signed main(){
    read(a);
    for(int i=1;i<=a*a*a;i++){
        read(g[i]);
        getline(cin,s);
        stringstream ss(s);
        int x;
        while(ss>>x) e[i].push_back(x);
    }
    for(int i=1;i<=a*a*a;i++){
        if(g[i]) continue;
        memset(dis,0x3f,sizeof(dis)),memset(flag,0,sizeof(flag)),memset(can,0,sizeof(can));
        int bh=0;dis[i]=0;can[i]=1;
        for(int v:e[i]){
            bh++;
            flag[v]=bh,dis[v]=1;
            q.push(v);
        }
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int v:e[u]){
                if(dis[v]==dis[u]+1) can[v]=1;
                if(dis[v]>dis[u]+1){
                    dis[v]=dis[u]+1;
                    flag[v]=flag[u];
                    q.push(v);
                }
            }
        }
        cnt_id++;
        cnt_fx[cnt_id]=bh;
        for(int j=1;j<=a*a*a;j++){
            if(can[j]) continue;
            hv[cnt_id][flag[j]][++cnt_hv[cnt_id][flag[j]]]=j;
        }
    }
    dfs(1);
    write(ans1,ans2);
    return 0;
}
posted @ 2026-03-24 22:28  Link-Cut_Trees  阅读(13)  评论(0)    收藏  举报