GDOI Training #458. 青青草原的表彰大會「NOIP多校聯考 2019」

題目鏈接:傳送門

青青草原上有kk只羊,他們聚集在包包大人的家裏,舉辦一年一度的表彰大會,在這次的表彰大會中,包包大人讓羊們按自己的貢獻從小到大排成一排,以便於發放獎金。每隻羊都會得到數值在 1~n 的獎金,並且第 ii 只羊的獎金應爲第 i+1i+1只羊的約數(即滿足 aiai+1a_i|a_{i+1})。現在包包大人想知道一共有多少種不同的發放獎金的方式(兩種發放獎金的方式不同是指在兩種發放獎金的方式中存在某隻羊拿到的獎金不同)

輸入格式
一行兩個正整數 n,k,滿足1n,k10000001\leq n,k\leq 1000000

輸出格式
一行一個整數代表發放獎金的方案對 1000000007 取模的結果

首先我們先寫出一個O(knlogn)O(knlogn)的大莉dp:
dp[i][j]dp[i][j]表示前ii只羊,且第ii只羊的獎金爲jj的方案數。
顯然的方程:dp[i][j]=kjdp[i1][k]dp[i][j]=\sum_{k|j}dp[i-1][k]
也就是說,dp[i]=dp[i1]Idp[i]=dp[i-1]*I*表示狄利克雷卷積,II表示常函數。狄利克雷卷積 & 莫比烏斯反演簡介
又因爲dp[1]=Idp[1]=I,所以dp[k]=Ikdp[k]=I^k
所以快速冪一下,O(nlogn)O(nlogn)大莉求狄利克雷卷積,總複雜度O(nlognlogk)O(nlognlogk),不知道爲什麼能卡過去……跑得好像還挺快的qwq

毒瘤代碼

#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<vector>
#define re register int
#define mod 1000000007
using namespace std;
typedef long long ll;
int read() {
	re x=0,f=1;
	char ch=getchar();
	while(ch<'0' || ch>'9') {
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9') {
		x=(x<<1)+(x<<3)+ch-'0';
		ch=getchar();
	}
	return x*f;
}
inline void write(const int x) {
	if(x>9)	write(x/10);
	putchar(x%10+'0');
}
const int Size=1000005;
const int INF=0x3f3f3f3f;
int n,k,Div[Size],I[Size],ans[Size],tmp[Size];
void mul(int *a,int *b,int *c) {	//a卷積b 
	for(re i=1; i<=n; i++)	c[i]=0;
	for(re i=1; i<=n; i++) {
		int maxj=Div[i];
		for(re j=1; j<=maxj; j++) {
			c[i*j]=(c[i*j]+(ll)a[i]*b[j])%mod;
		}
	}
}
int main() {
//	freopen("T3.in","r",stdin);
//	freopen("3.out","w",stdout);
	n=read();
	k=read()-1;
//	n=k=1e6;
	for(re i=1; i<=n; i++) {
		I[i]=ans[i]=1;
		Div[i]=n/i;
	}
	int siz=(n+2)<<2;
	while(k) {
		if(k&1) {
			mul(ans,I,tmp);
			memcpy(ans,tmp,siz);
		}
		mul(I,I,tmp);
		memcpy(I,tmp,siz);
		k>>=1;
	}
	int tot=0;
	for(re i=1; i<=n; i++) {
		tot=(tot+ans[i])%mod;
	}
	printf("%d",tot);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章