1. 程式人生 > >【WC2017模擬1.22】簡單題

【WC2017模擬1.22】簡單題

題目大意

給定n,k,求把n!拆分成k個不同的正整數的乘積的方案數。(一種方案的排列仍是一種方案)。答案對109+7取模。

n≤10000 k≤30

時限為4s

分析

這是一道容斥好題。

首先可以不管算重,最終答案除以k!即可。

接下來考慮如何容斥。k個數互不相同,其實就相當於k(k1)2個限制條件。如果一個方案不滿足x個條件,那麼它要乘上的容斥係數為(1)x

如果直接列舉不滿足哪些,顯然會超時。不妨轉換一下:已知條件是形如(x,y)的,表示第x個數≠第y個數。把k個數看成k個點,限制(x,y)看成連線x,y的邊。那麼一個選擇方案相當於選擇一些邊,使其變成許多個聯通塊。

好了,現在可以嘗試列舉每個聯通塊的大小。這就相當於對k的整數拆分。為了避免算重,當前列舉的這一個數必須不小於前一個。跑出來,30能有5000多種拆法。那麼整數拆分的方案數就可以接受了。接下來到下一步。

現在已經確定了k的一個整數拆分方案(即若干個聯通塊大小)。假設有m個聯通塊,第i個大小為si。它對答案的貢獻是多少?這要考慮三個東西:
1. 把k個點分配到這些聯通塊的方案數。
2. 對應的分配邊的所有方案對答案的貢獻
3. 拆分n!,滿足形成這些聯通塊的方案數。
它對答案的貢獻就是三個數的乘積。現在一個個來考慮。

首先是第1部分

這個比較簡單。首先總的排列數有k!個。設在當前的整數拆分方案中,數字i出現了pi次,即ipi=k。那麼對於拆出來的每個聯通塊si,這表示有si個數相同,就要除掉si!,接下來由於i出現了pi次,又要除掉pi!。最終得到:

Ans1=k!k
i=1
((i!)aiai!)

第2部分

對於每個聯通塊分別考慮。因為一條邊的意義是兩個數相同,所以一個聯通塊裡的所有數都是相等的。那就可以直接考慮容斥係數的總和了。
S(n)表示n個點的聯通圖的集合,e(G)G。對於第i個聯通塊(大小為si),它對答案的貢獻就是GS(si)(1)e(G)
g(n)等於n個點的聯通圖對答案的貢獻。這個可以考慮用容斥計算,即全集減去不合法方案數
首先考慮不合法的。其實就是n個點的不聯通圖。它形成了至少2個聯通塊。列舉編號為1的點所在的聯通塊大小,那麼:

g(n)=f(n)i=1n1Cin1f(ni)g(i)
其中f
(n)
表示n個點的全集大小。

考慮f(n)的值。顯然f(1)=1。當n>1,就會有n(n1)2條可能出現的邊。如果其中一條邊出現了,容斥係數就乘-1,否則乘1。所有方案加起來後發現,當n>1時,f(n)=0!
上面的式子變成:

g(n)={0(n1)g(n1),1,n>1n=1
那麼一個整數拆分方案第2部分的貢獻為Ans2=i=1mg(si)

最後是第3部分

首先給n!分解質因數。每個質因子都是相對獨立的,就可以分開考慮。
對於一個質因子,設它共出現了cnt次,現在要把它分配到m個聯通塊裡。一個聯通塊裡的數要滿足相同,每個質因子出現個數也一定要相同。所以對於si,它要在cnt份裡佔掉si的倍數份(可以是0)。這就是個完全揹包問題了。

優化

為了提高執行效率,可能需要一些優化。
1. 遞迴對k進行拆分,然後做揹包時相當於有m個物品。回溯的時候,前m-1個物品的答案要保留,不用每個方案都重新做一次dp
2. 假設k當前剩下x沒有拆分,先列舉到x/2,再直接整個拆分
3. 適當去掉一些模運算

我的程式極限資料跑了3s左右

#include <cstdio>
#include <cstring>
#include <algorithm>

#define Min(a,b) ((a)<(b)?(a):(b))

using namespace std;

const int N=10005,M=31,mo=1e9+7,NN=100000;

typedef long long LL;

int n,m,Fac[N],Inv[N],F_Inv[N],g[N],tot,p[N],f[M][NN],cnt[N],st[N],sum,ans;

bool bz[N];

void init()
{
    scanf("%d%d",&n,&m);
    for (int i=2;i<=n;i++)
    {
        if (!bz[i]) p[tot++]=i;
        for (int j=0;j<tot && i*p[j]<=n;j++)
        {
            bz[i*p[j]]=1;
            if (i%p[j]==0) break;
        }
    }
    for (int i=2;i<=n;i++)
        for (int j=0,x=i;j<tot;j++)
            for (;x%p[j]==0;x/=p[j]) cnt[j]++;
    for (int i=1;i<=tot;i++) st[i]=st[i-1]+cnt[i-1]+1;
    Fac[0]=F_Inv[0]=Fac[1]=Inv[1]=F_Inv[1]=1;
    for (int i=2;i<=n;i++)
    {
        Fac[i]=(LL)Fac[i-1]*i%mo;
        Inv[i]=(LL)Inv[mo%i]*(mo-mo/i)%mo;
        F_Inv[i]=(LL)F_Inv[i-1]*Inv[i]%mo;
    }
}

void dfs(int x,int y,int m,int sum,int t)
{
    if (m==0)
    {
        for (int i=0;i<tot;i++) sum=(LL)sum*f[x-1][st[i+1]-1]%mo;
        ans=(ans+sum)%mo;
        return;
    }
    if (y>m) return;
    for (int i=0;i<tot;i++)
    {
        for (int j=st[i];j<st[i]+y;j++) f[x][j]=f[x-1][j];
        for (int j=st[i]+y;j<st[i+1];j++) f[x][j]=(f[x-1][j]+f[x][j-y])%mo;
    }
    dfs(x+1,y,m-y,(LL)sum*F_Inv[y]%mo*Inv[t+1]%mo*g[y]%mo,t+1);
    if (y==m) return;
    for (int k=y+1;k*2<=m;k++)
    {
        for (int i=0;i<tot;i++)
        {
            f[x][st[i]+k-1]=f[x-1][st[i]+k-1];
            for (int j=st[i]+k;j<st[i+1];j++) f[x][j]=(f[x-1][j]+f[x][j-k])%mo;
        }
        dfs(x+1,k,m-k,(LL)sum*F_Inv[k]%mo*g[k]%mo,1);
    }
    for (int i=0;i<tot;i++)
    {
        for (int j=st[i];j<st[i]+m;j++) f[x][j]=f[x-1][j];
        for (int j=st[i]+m;j<st[i+1];j++) f[x][j]=(f[x-1][j]+f[x][j-m])%mo;
    }
    dfs(x+1,m,0,(LL)sum*F_Inv[m]%mo*g[m]%mo,1);
}

void work()
{
    g[1]=1;
    for (int i=2;i<=m;i++) g[i]=(-(LL)(i-1)*g[i-1])%mo;
    ans=0;
    for (int i=0;i<tot;i++) f[0][st[i]]=1;
    dfs(1,1,m,Fac[m],0);
    ans=(LL)(ans+mo)%mo*F_Inv[m]%mo;
    printf("%d\n",ans);
}

int main()
{
    //freopen("jdt.in","r",stdin); freopen("jdt.out","w",stdout);
    init();
    work();
    fclose(stdin); fclose(stdout);
    return 0;
}

相關推薦

WC2017模擬1.22簡單

題目大意 給定n,k,求把n!拆分成k個不同的正整數的乘積的方案數。(一種方案的排列仍是一種方案)。答案對109+7取模。 n≤10000 k≤30 時限為4s 分析 這是一道容斥好題。 首先可以不管算重,最終答案除以k!即可。 接下來考慮

GDOI2016模擬4.22總結

ble 走了 一見 打了 再看 很多 這不 學會 由於 前言 早上,一進機房,發現所有人神情嚴肅,一股(\(da\))(\(ba\))場的氣氛迎面撲來,我一下子意識到:nothing good! 這場比賽結果不是很好,50分; 第一題:感覺上是個神奇的匹配問題,但是,由於

jzoj5919. NOIP2018模擬10.22逛公園(tarjan,二分)

5919. 【NOIP2018模擬10.22】逛公園 Description 琥珀色黃昏像糖在很美的遠方,思念跟影子在傍晚一起被拉長…… Description 小 B 帶著 GF 去逛公園,公園一共有 n 個景點,標號為 1 . . . n。景點之間有 m

JZOJ5919. NOIP2018模擬10.22逛公園

Description 小 B 帶著 GF 去逛公園,公園一共有 n 個景點,標號為 1 . . . n。景點之間有 m 條路徑相連。 小 B 想選擇編號在一段區間 [l, r] 內的景點來遊玩,但是如果這些景點的誘導子圖形成了環,那麼 GF 將會不高興。 小 B 給出很多個詢問 [x,

JZOJ-senior-5920. NOIP2018模擬10.22風箏

Time Limits: 4000 ms Memory Limits: 524288 KB Description 當一陣風吹來,風箏飛上天空,為了你,而祈禱,而祝福,而感動…… oyiya 在 AK 了 IOI 之後來到了鄉下,在田野中玩耍,放鬆身心。 他發

JZOJ-senior-5921. NOIP2018模擬10.22種花

Time Limits: 2000 ms Memory Limits: 524288 KB Description 院子落葉,跟我的思念厚厚一疊;窗臺蝴蝶,像詩裡紛飛的美麗章節…… Input 小 H

5921. NOIP2018模擬10.22種花

題目大意 因為找不到題面所以就勉為騎♂男C了一張下來 思路 因為題目要求的是最後的總和,所以可以分開每一對a[i]和a[j]來考慮,計算每一對的貢獻 對於每一對數,考慮其位置和出現次數,可以分成幾類出現次數相同的來討論 那麼每一類的貢獻就是 (距離1+距離2+……)*出現次

NOIP2015模擬10.22最小代價

前言 本來在比賽上就想到最小生成樹了,但不相信這道題那麼簡單,然後就沒有然後了。。。 題目 給出一幅由n個點m條邊構成的無向帶權圖。 其中有些點是黑點,其他點是白點。 現在每個白點都要與他距離最近的黑點通過最短路連線(如果有很多個黑點,可以選取其中

JZOJ3973NOI2015模擬1.10NOI2013湖南省隊集訓黑白樹(wbtree)(並查集)

Problem   給定一棵樹,邊的顏色為黑或白,初始時全部為白色。維護兩個操作: 1. 查詢u到根路徑上的第一條黑色邊的標號。 2. 將u到v路徑上的所有邊的顏色設為黑色。 Hint   對於30%的資料:n,m≤103n,m≤103   對於

jzoj5986. WC2019模擬2019.1.4立體幾何 (權值線段樹)

傳送門 題面 題解 不難看出每個點的大小為行列限制中較小的那一個(因為資料保證有解) 對於行的每個限制,能取到的個數是列裡限制大於等於它的數的個數,同理,對於列是行裡大於它的個數(這裡沒有等於,為了避免重複計算) 於是可以對於行列分別開權值線段樹,修改的時候只要把對應的貢獻改一下就好了 //mi

BZOJ2683簡單 [分治][樹狀數組]

math 所有 操作數 def 正整數 || lap ref 維護 簡單題 Time Limit: 50 Sec Memory Limit: 128 MB[Submit][Status][Discuss] Description   你有一個N*N的棋盤,每

bzoj3687簡單

#3687. 簡單題 記憶體限制:512 MiB 時間限制:10 Sec 提交 提交記錄 討論 題目描述 小呆開始研究集合論了,他提出了關

JZOJ5612. NOI2018模擬3.29第3

題意: 資料範圍: Analysis: 20分很顯然的設 f i

JZOJ 5878. NOIP2018提高組模擬9.22電路圖 A

Description nodgd 要畫一個電路圖。 這是一個很簡單的電路圖,所有的元件都是串聯關係,從整體來看就是一個環狀的結構。畫電路圖有很多要求,nodgd 為了畫得好看就又添加了一些 額外的要求。所有要求歸結起來有以下幾點: 1、這個環狀電路上有n個雙端

JZOJ-senior-5878. NOIP2018提高組模擬9.22電路圖 A

Time Limits: 1000 ms Memory Limits: 262144 KB Description nodgd 要畫一個電路圖。 這是一個很簡單的電路圖,所有的元件都是串聯關係,從整體

BZOJ 3687簡單

題目描述 小呆開始研究集合論了,他提出了關於一個數集四個問題: 1.子集的異或和的算術和。 2.子集的異或和的異或和。 3.子集的算術和的算術和。 4.子集的算術和的異或和。 目前為止,小呆已經解決了前

LG4148簡單

【LG4148】簡單題 題面 洛谷 題解 \(kdt\)模板題呀。。。 #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <

樹狀陣列CQOI2006簡單

【題目描述】         有一個n個元素的陣列,每個元素初始均為0。有m條指令,要麼讓其中一段連續序列數字反轉——0變1,1變0(操作1),要麼詢問某個元素的值(操作2)。例如當n=20時,10條指令如下: 【輸入格式】     第一行包含兩個整數n,m

凸優化長鏈剖分2019冬令營模擬1.8tree

PROMBLEM 給你一棵樹,你需要在樹上選擇恰好 m條點不相交的、長度至少為 k的路徑,使得路徑所覆蓋的點權和儘可能大。求最大點權和。 資料保證有解。 SOLUTION 這是一道綜合的題目,考察凸優化、長鏈剖分、樹形DP、以及關於陣列空間的優化 首先引進凸優