HAOI2018 簡要題解

HAOI2018 簡要題解

R1T1 奇怪的揹包

題意

小C很是擅長揹包問題,他有一個奇怪的揹包,這個揹包有一個參數 \(P\) ,當他向這個揹包內放入若干個物品後,揹包的重量是物品整體積對 \(P\) 取模後的結果.html

如今小C有 \(n\) 種體積不一樣的物品,第 \(i\) 種佔用體積爲 \(V_i\) ,每種物品都有無限個.他會進行 \(q\) 次詢問,每次詢問給出重量 \(w_i\) ,你須要回答有多少种放入物品的方案,能將一個初始爲空的揹包的重量變爲 \(w_i\) .注意,兩種方案被認爲是不一樣的,當且僅當放入物品的種類不一樣,而與每種物品放入的個數無關.不難發現總的方案數爲 \(2^n\) .ios

因爲答案可能很大,你只須要輸出答案對 \(10^9 + 7\) 取模的結果.c++

img

題解

這題耐着性子打:測試一、7是人口普查,測試234暴力枚舉選或不選DP轉移git

能夠發現一個性質:\(V[i]=gcd(V[i],P)\)github

而後若是選的集合是S,則須要\(gcd(V[i],P)(i\in S)|gcd(w,P)\),才能知足條件。這個瞪了很久沒有瞪出來。數組

原理是這樣的:若是選了兩個\(V[i],V[j]\)(已經對P取了\(gcd\)),那麼能夠表示\(P-V[i]+V[j]\),也就是說,能夠當作多選了一個\(V[j]-V[i]\),同理這樣能夠找到\(gcd(V[i],V[j])\)ide

有一個結論:兩億之內,約數最多的數的約數個數爲1536。因此給P的約數編個號,設\(dp[i][j]\)表示在前面\(i\)個數中、選出的數的\(gcd\)爲第\(j\)個約數,的選數的方案數。最後統計\(Ans[i]\)表示\(gcd(P,w)=\)第i個約數 的對應\(w\)的答案。測試

代碼

直接看Work5就行了。ui

#include<iostream>
#include<queue>
#include<cstring>
#include<map>
#include<algorithm>
using namespace std;
int read()
{
    char ch=getchar();int h=0;
    while(ch>'9'||ch<'0') ch=getchar();
    while(ch>='0'&&ch<='9') h=h*10+ch-'0',ch=getchar();
    return h;
}
int gcd(int a,int b) {return b?gcd(b,a%b):a;}
const int N=1e6+10,mod=1e9+7;
int n,q,P,V[N],w[N],bit[N];
void Work1()
{
    int d=gcd(V[1],P),x;
    while(q--) x=read(),printf("%d\n",x%d==0?1:0);
    exit(0);
}
void Work2()
{
    for(int i=1;i<=q;i++)
        printf("%d\n",(bit[n]+mod-1)%mod);
    exit(0);
}
void Work3()
{
    queue<int> Q;
    int f[300],ans[300],to[20],tt=0;
    memset(ans,0,sizeof(ans));
    memset(f,0,sizeof(f));
    for(int zt=0;zt<1<<n;zt++)
    {
        tt=0;
        for(int i=1;i<=n;i++)
            if(((1<<(i-1))&zt)&&f[V[i]]!=zt)
                f[V[i]]=zt,Q.push(V[i]),to[++tt]=V[i];
        while(!Q.empty())
        {
            int x=Q.front();Q.pop();ans[x]++;
            for(int i=1;i<=tt;i++)
                if(f[(x+to[i])%P]!=zt) f[(x+to[i])%P]=zt,Q.push((x+to[i])%P);
        }
    }
    for(int i=1,x;i<=q;i++)
        x=read(),printf("%d\n",ans[x]);
    exit(0);
}
int dp[1001][10010];
void Work4()
{
    dp[0][0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<P;j++) dp[i][j]=dp[i-1][j];
        for(int j=0;j<P;j++)
            (dp[i][gcd(V[i],j)]+=dp[i-1][j])%=mod;
    }
    for(int i=1,x,ans;i<=q;i++)
    {
        x=read();ans=0;
        for(int j=1;j*j<=x;j++)
            if(x%j==0)
            {
                (ans+=dp[n][j])%=mod;
                if(j*j!=x) (ans+=dp[n][x/j])%=mod;
            }
        printf("%d\n",ans); 
    }
    exit(0);
}
int f[2000][2000],Ans[2000];
int W[2000],cof[2000],id[2000];
map<int,int> to;
void Work5()
{
    int tt=0,u=0;
    f[0][0]=1;to[0]=0;
    for(int i=1;i*i<=P;i++)
        if(P%i==0)
        {
            to[i]=++tt;id[tt]=i;
            if(i*i!=P) to[P/i]=++tt,id[tt]=P/i;
        }
    sort(V+1,V+n+1);
    for(int i=1;i<=n;i++)
        if(V[i]!=V[i-1]) W[++u]=V[i],cof[u]=1;
        else cof[u]++;
    for(int i=1;i<=u;i++) cof[i]=bit[cof[i]]-1;
    for(int i=1;i<=u;i++)
    {
        for(int j=0;j<=tt;j++) f[i][j]=f[i-1][j];
        for(int j=0;j<=tt;j++)
            (f[i][to[gcd(id[j],W[i])]]+=1ll*cof[i]*f[i-1][j]%mod)%=mod;
    }
    for(int i=1,x,ans;i<=tt;i++)
    {
        x=id[i];ans=0;
        for(int j=1;j<=tt;j++)
            if(x%id[j]==0) (ans+=f[u][j])%=mod;
        Ans[i]=ans;
    }
    for(int i=1,x;i<=q;i++)
        x=gcd(read(),P),printf("%d\n",Ans[to[x]]);
}
int main()
{
    cin>>n>>q>>P;
    bit[0]=1;
    for(int i=1;i<=n;i++) V[i]=read(),V[i]=gcd(V[i],P);
    for(int i=1;i<=n;i++) bit[i]=2ll*bit[i-1]%mod;
    if(n==1) Work1();
    if(P==998244353) Work2();
    if(P<=250&&n<=10) Work3();
    if(P<=10000) Work4();
    Work5();
}

R1T2反色遊戲

題意

小C和小G常常在一塊兒研究搏弈論問題,有一天他們想到了這樣一個遊戲.spa

有一個 \(n\) 個點 \(m\) 條邊的無向圖,初始時每一個節點有一個顏色,要麼是黑色,要麼是白色.如今他們對於每條邊作出一次抉擇:要麼將這條邊鏈接的兩個節點都反色(黑變白,白變黑),要麼不做處理.他們想把全部節點都變爲白色,他們想知道在 \(2^m\) 種決策中,有多少種方案能達成這個目標.

小G認爲這個問題太水了,因而他還想知道,對於第 \(i\) 個點,在刪去這個點及與它相連的邊後,新的答案是多少.

因爲答案可能很大,你只須要輸出答案對 \(10^9 + 7\) 取模後的結果.

img

題解

耐着性子打30分暴力。

對於這種翻轉顏色,能夠想到異或方程組,而後答案就是\(2^{自由元}\)。可是因爲詢問過多,不會動態維護,只能GG。

考慮正解:對於一個聯通塊來講,若是構成一棵樹,那麼方案惟一;若是不是一棵樹,對於\(m-n+1\)條非樹邊,能夠選也能夠不選,選了就對應選樹上路徑使得沒有影響,方案數爲\(2^{m-n+1}\)

若是有一個聯通塊有奇數個黑點,那麼答案爲0;不然答案爲\(2^{m-n+cc}\)\(cc\)爲聯通塊個數。

怎麼去刪點呢?咱們發現只有割點影響聯通塊劃分,因而刪去點\(i\)須要考慮如下狀況:

  • \(i\)度數爲0,自成一個聯通塊
  • \(i\)不是割點,須要看聯通塊的黑點數的奇偶性
  • \(i\)是割點,須要看被劃分出來的各個聯通塊的黑點數是否存在奇數

所有用Tarjan實現。具體實現細節有點多,看代碼好了。

代碼

#include<iostream>
#include<cstring>
#include<bitset>
using namespace std;
const int N=1e5+10,mod=1e9+7;
struct Edge{int fr,to;}E[N];
int n,m;
char s[N];
namespace cpp1
{
    int dp[51][1<<15],bit[20],tag[51];
    void Calc()
    {
        memset(dp,0,sizeof(dp));
        int start=0;
        for(int i=1;i<=n;i++) start|=bit[i]*(s[i]-'0');     
        dp[0][start]=1;
        for(int i=1;i<=m;i++)
            for(int zt=0;zt<bit[n+1];zt++)
            {
                dp[i][zt]=dp[i-1][zt];
                if(!tag[i]) (dp[i][zt]+=dp[i-1][zt^bit[E[i].fr]^bit[E[i].to]])%=mod;
            }
        printf("%d ",dp[m][0]);
    }
    void main()
    {
        for(int i=1;i<=n+1;i++) bit[i]=1<<(i-1);
        Calc();
        for(int i=1;i<=n;i++)
        {
            char tmp=s[i];s[i]='0';
            for(int j=1;j<=m;j++)
                if(E[j].fr==i||E[j].to==i) tag[j]=1;
            Calc();s[i]=tmp;
            for(int j=1;j<=m;j++) tag[j]=0;
        }
        puts("");
    }
}
namespace cpp2
{
    struct edge{int next,to;}a[N<<1];
    int tag[N],dfn[N],low[N],tot,head[N],cnt,g[N];
    int vis[N],siz[N],cc,ww,d[N],du[N],bit[N<<1];
    void link(int x,int y)
    {
        du[x]++;du[y]++;
        a[++cnt]=(edge){head[x],y};head[x]=cnt;
        a[++cnt]=(edge){head[y],x};head[y]=cnt;
    }
    void Min(int &x,int y) {if(y<x) x=y;}
    void Tarjan(int x,int fr,int rt)
    {
        int ss=0;dfn[x]=low[x]=++tot;
        siz[x]=(s[x]=='1');vis[x]=rt;
        for(int i=head[x];i;i=a[i].next)
        {
            int R=a[i].to;if(R==fr) continue;
            if(!dfn[R])
            {
                Tarjan(R,x,rt);siz[x]+=siz[R];
                ss++;Min(low[x],low[R]);
                if(low[R]>=dfn[x]) tag[x]++,d[x]|=siz[R]&1,g[x]+=siz[R];
            }
            else Min(low[x],dfn[R]);
        }
        if(!fr) tag[x]--;
    }
    void main()
    {
        memset(head,0,sizeof(head));
        memset(vis,0,sizeof(vis));
        memset(dfn,0,sizeof(dfn));
        memset(d,0,sizeof(d));
        memset(g,0,sizeof(g));
        memset(tag,0,sizeof(tag));
        memset(du,0,sizeof(du));
        bit[0]=1;cnt=tot=cc=ww=0;
        for(int i=1;i<=m;i++) link(E[i].fr,E[i].to);
        for(int i=1;i<=n;i++)
            if(!dfn[i]) cc++,Tarjan(i,0,i),ww+=(siz[i]&1);
        for(int i=1;i<=m*2;i++) bit[i]=bit[i-1]*2ll%mod;
        printf("%d ",ww?0:bit[m-n+cc]);
        for(int i=1;i<=n;i++)
            if(ww-(siz[vis[i]]&1)) printf("0 ");
            else if(d[i]) printf("0 ");
            else if((siz[vis[i]]-(s[i]=='1')-g[i])&1) printf("0 ");
            else printf("%d ",bit[m-n+cc+tag[i]+1-du[i]]);
        puts("");
    }
}
void Work()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++) scanf("%d%d",&E[i].fr,&E[i].to);
    scanf("%s",s+1);
    if(n<=15&&m<=50) cpp1::main();
    else cpp2::main();
}
int main() {int T;cin>>T;while(T--) Work();}

R1T3 字串覆蓋

題意

小C對字符串很有研究,他以爲傳統的字符串匹配太無聊了,因而他想到了這樣一個問題.

對於兩個長度爲 \(n\) 的串 \(A, B\) , 小C每次會給出給出 \(4\) 個參數 \(s, t, l, r\) . 令 \(A\)\(s\)\(t\) 的子串(從 \(1\) 開始標號)爲 \(T\),令 \(B\)\(l\)\(r\) 的子串爲 \(P\).而後他會進行下面的操做:

若是 \(T\) 的某個子串與 \(P\) 相同,咱們就能夠覆蓋 \(T\) 的這個子串,並得到 \(K - i\) 的收益,其中 \(i\) 是初始時 \(A\) 中(注意不是 \(T\) 中)這個子串的起始位置,\(K\)是給定的參數.一個位置不能被覆蓋屢次.覆蓋操做能夠進行任意屢次,你須要輸出得到收益的最大值.

注意每次詢問都是獨立的,即進行一次詢問後,覆蓋的位置會復原.

img

題解

暴力30分,眼刷了。再沒思路。

可是題目提示得很明顯:按照\(l\)的大小分別作。

orz出題人:https://dy0607.github.io/%E7%9C%81%E9%80%89/oi%E5%8E%86%E7%A8%8B/2018/04/24/HAOI2018-Round1-%E8%A7%A3%E9%A2%98%E6%8A%A5%E5%91%8A.html

對於詢問串長>50的串:

答案會很小,因而能夠暴力一個一個加答案,過程以下:

  • 求出SA數組,倍增出該詢問的左右端點(也就是排名爲\([l,r]\)間的串的lcp大於詢問串長)。
  • 查位置在\([s,t]\)(也就是給你的母串)中、排名在\([l,r]\)的位置最小的點,累加答案。

對於詢問串長<=50的串:

對每種長度的詢問串分別計算,把每一個點向右邊第一個表示串相同、不相交的點連邊。連成一個森林結構,倍增統計答案便可。

代碼

#include<iostream>
#include<cstring>
#include<algorithm>
#define ll long long
#define log2 caijixzy
using namespace std;
const int N=3e5+10;
int n,K;
char S[N],s[N];

//Part 1 Get SA
int l,SA[N],rk[N],x[N],y[N],t[N],m,h[N];
int cmp(int i,int j,int k) {return y[i]==y[j]&&y[i+k]==y[j+k];}
void Sort()
{
    for(int i=1;i<=m;i++) t[i]=0;
    for(int i=1;i<=l;i++) t[x[i]]++;
    for(int i=1;i<=m;i++) t[i]+=t[i-1];
    for(int i=l;i>=1;i--) SA[t[x[y[i]]]--]=y[i];
}
void GetSA()
{
    m=1000;l=n*2+1;
    for(int i=1;i<=l;i++) x[i]=s[i],y[i]=i;Sort();
    for(int k=1,p=0;k<=l;k<<=1)
    {
        for(int i=l-k+1;i<=l;i++) y[++p]=i;
        for(int i=1;i<=l;i++) if(SA[i]>k) y[++p]=SA[i]-k;
        Sort();swap(x,y);x[SA[1]]=p=1;
        for(int i=2;i<=l;i++) x[SA[i]]=cmp(SA[i],SA[i-1],k)?p:++p;
        if(p==l) break;m=p,p=0;
    }
    for(int i=1;i<=l;i++) rk[SA[i]]=i;
    for(int i=1,j=0;i<=l;i++)
    {
        while(s[i+j]==s[SA[rk[i]-1]+j]) j++;
        h[rk[i]]=j;if(j) j--;
    }
}

//Part 2 Get answers <=50
struct Que{int s,t,l,len,id,pos;}Q[N];
int q,pos[N],log2[N],fa[N][20];
ll sum[N][20],Ans[N];
int cmp1(const Que&A,const Que&B) {return A.len==B.len?A.pos<B.pos:A.len<B.len;}
void Calc(int len,int &it)
{
    if(Q[it].len!=len) return;
    for(int l=1,r=1,cc=0;l<=2*n+1;r=l=r+1,cc=0)
    {
        while(h[r]>=len) ++r;
        for(int i=l;i<=r;i++) if(SA[i]<=n) pos[++cc]=SA[i];
        if(Q[it].pos>r||Q[it].pos<l) continue;
        sort(pos+1,pos+cc+1);pos[cc+1]=2*n+2;
        int up=log2[min(n/len,cc)],k=1;
        for(int i=1;i<=cc;i++)
        {
            while(pos[k]-pos[i]<len) k++;
            fa[i][0]=k;sum[i][0]=K-pos[i];
        }
        for(int p=1;p<=up;p++)
            for(int i=1;i<=cc;i++)
                if(fa[fa[i][p-1]][p-1])
                {
                    fa[i][p]=fa[fa[i][p-1]][p-1];
                    sum[i][p]=sum[i][p-1]+sum[fa[i][p-1]][p-1];
                }
        for(ll res=0;Q[it].len==len&&Q[it].pos<=r;it++,res=0)
        {
            int s=Q[it].s,t=Q[it].t,x=lower_bound(pos+1,pos+cc+2,s)-pos;
            if(pos[x]>t) continue;
            for(int p=up;p>=0;p--)
                if(pos[fa[x][p]]<=t&&pos[fa[x][p]])
                    res+=sum[x][p],x=fa[x][p];
            Ans[Q[it].id]=res+sum[x][0];
        }
        for(int i=1;i<=cc;i++)
            for(int p=0;p<=up;p++) fa[i][p]=0,sum[i][p]=0;
    }
}

//Part 3 Get answers >50
int ST[N][20],rt[N],nod;
struct President_Tree
{
    #define lc t[x].ch[0]
    #define rc t[x].ch[1]
    struct seg{int ch[2],siz;}t[N*30];
    void Add(int &x,int l,int r,int p)
        {
            t[++nod]=t[x];t[x=nod].siz++;
            if(l==r) return;
            int mid=(l+r)>>1;
            if(p<=mid) Add(lc,l,mid,p);
            else Add(rc,mid+1,r,p);
        }
    int Query(int R,int L,int l,int r,int p)
        {
            if(t[R].siz-t[L].siz==0) return 0;
            if(l==r) return l;
            int mid=(l+r)>>1,res=0;
            int sizl=t[t[R].ch[0]].siz-t[t[L].ch[0]].siz;
            if(p<=mid&&sizl) res=Query(t[R].ch[0],t[L].ch[0],l,mid,p);
            if(!res) res=Query(t[R].ch[1],t[L].ch[1],mid+1,r,p);
            return res;
        }
}T;
int main()
{
    //Part 1 Init and build sa
    cin>>n>>K;
    scanf("%s%s",s+1,S+1);s[n+1]='#';
    for(int i=n+2;i<=n+n+1;i++) s[i]=S[i-n-1];
    GetSA();cin>>q;
    for(int i=1;i<=n*2+1;i++) h[i]=h[i+1];
    for(int i=1;i<=q;i++)
    {
        int s,t,l,r;scanf("%d%d%d%d",&s,&t,&l,&r);
        Q[i]=(Que){s,t-(r-l),l,r-l+1,i,rk[l+n+1]};
    }

    //Part 2 Get answers <=50
    int it=1;
    log2[1]=0;
    for(int i=1;i<=n*2+1;i++) log2[i]=log2[i>>1]+1;
    sort(Q+1,Q+q+1,cmp1);
    for(int i=1;i<=min(n,50);i++) Calc(i,it);

    //Part 3 Get answers >50
    for(int i=1;i<=n*2+1;i++) ST[i][0]=h[i];
    for(int p=1;p<=log2[2*n+1];p++)
        for(int i=1;i<=n*2+1;i++)
            ST[i][p]=min(ST[i][p-1],ST[i+(1<<(p-1))][p-1]);
    for(int i=1;i<=n*2+1;i++)
    {
        rt[i]=rt[i-1];
        if(SA[i]<=n) T.Add(rt[i],1,n,SA[i]);
    }
    for(;it<=q;it++)
    {
        int l,r;l=r=Q[it].pos;
        for(int p=log2[n];p>=0;p--)
            if(l-(1<<p)>=1&&ST[l-(1<<p)][p]>=Q[it].len) l=l-(1<<p);
        for(int p=log2[n];p>=0;p--)
            if(r+(1<<p)<=n*2+1&&ST[r][p]>=Q[it].len) r=r+(1<<p);
        for(int p=Q[it].s;p<=Q[it].t;)
        {
            int nw=T.Query(rt[r],rt[l-1],1,n,p);
            if(nw>Q[it].t||!nw) break;
            Ans[Q[it].id]+=K-nw;
            p=nw+Q[it].len;
        }
    }
    for(int i=1;i<=q;i++) printf("%lld\n",Ans[i]);
    return 0;
}

R2T1 蘋果樹

題意

小C在本身家的花園裏種了一棵蘋果樹,樹上每一個結點都有剛好兩個分支。通過細心的觀察,小C發現每一天這棵樹都會生長出一個新的結點。

第一天的時候, 果樹會長出一個根結點,之後每一天,果樹會隨機選擇一個當前樹中沒有長出過結點的分支,
而後在這個分支上長出一個新結點,新結點與分支所屬的結點之間鏈接上一條邊。

小C定義一棵果樹的不便度爲樹上兩兩結點之間的距離之和,兩個結點之間的距離定義爲從一個點走到另外一個點的路徑通過的邊數。

如今他很是好奇,若是 \(N\) 天以後小G來他家摘蘋果,這個不便度的指望 \(E\) 是多少。可是小C討厭分數,,因此他只想知道 \(E \times N!\)\(P\) 取模的結果,能夠證實這是一個整數。

對於 \(20\%\) 的數據,\(N \le 10\)

對於 \(50\%\) 的數據,\(N \le 500\)

對於另外 \(20\%\) 的數據,\(P = 10^9 + 7\)

對於 \(100\%\) 的數據,\(N \le 2000, P \le 10^9 + 7\)

題解

題意就是全部二叉樹的答案之和。

轉而考慮每條邊(每一個點向父親連邊)的貢獻:

點對數:\(siz_i*(n-siz_i)\)
給定\(t=siz_i\),此時的樹的生成方式:\(i!C_{n-i}^{siz_i}siz_i!(i-2)(i-1)i(i+1)...(i-2+n-i-siz_i+1)\)

解釋一下樹的生成方式:

  • 對於前\(i\)個點的生成方式,顯然是\(i!\)
  • 在接下來的\(n-i\)個點中,選擇\(siz_i\)個點放進\(i\)的子樹,方案是\(siz_i!\)
  • 剩下的\(n-i-siz_i\)個點放在子樹外面,可放位置爲上述上升冪

因此枚舉每一條邊這麼算就行了。

代碼

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
const int N=2010;
int n,p,jc[N],C[N][N],ans;
int main()
{
    cin>>n>>p;jc[0]=C[0][0]=1;
    for(int i=1;i<=n;i++) jc[i]=1ll*jc[i-1]*i%p,C[i][0]=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%p;
    for(int i=2;i<=n;i++)
        for(int j=1;j<=n-i+1;j++)
        {
            int bs=1ll*j*(n-j)%p;
            int res=1ll*i*(i-1)%p*jc[n-j-1]%p;
            (ans+=1ll*C[n-i][j-1]*jc[j]%p*res%p*bs%p)%=p;
        }
    cout<<ans<<endl;
}

R2T2 染色

題意

爲了報答小 C 的蘋果, 小 G 打算送給熱愛漂亮術的小 C 一塊畫布,
這塊畫布能夠抽象爲一個長度爲 \(N\) 的序列, 每一個位置均可以被染成 \(M\) 種顏色中的某一種.

然而小 C 只關心序列的 \(N\) 個位置中出現次數剛好爲 \(S\) 的顏色種數,
若是剛好出現了 \(S\) 次的顏色有 \(K\) 種, 則小C會產生 \(W_k\) 的愉悅度.

小 C 但願知道對於全部可能的染色方案, 他能得到的愉悅度的和對 \(1004535809\) 取模的結果是多少.

對於 \(10\%\) 的數據,\(N \le 10, M \le 5\)

對於 \(30\%\) 的數據,\(N \le 100, M \le 100\)

對於另外 \(10\%\) 的數據,$ M \le 1000$;

對於另外 \(10\%\) 的數據,$ \forall 1 \le i \le M, W_i = 0$;

對於 \(100\%\) 的數據,\(N \le 10^7, M \le 10^5, S \le 150, 0 \le W_i < 1004535809\)

題解

確定要枚舉k,因而有kS個位置是要染成同一種顏色。

獲得式子:\(C(N,kS)*M*(M-1)^{N-kS}\),這顯然計算的是至少爲k的方案。

正好就是\(\sum_{j=k}^{lim}(-1)^{j-k}C(N,jS)*M*(M-1)^{N-jS}\),而後把式子拆開就能夠直接NTT了。

代碼

#include<iostream>
#include<algorithm>
using namespace std;
const int N=6e5+10,mod=1004535809,up=1e7;
int n,m,s,A[N],B[N],jc[up+10],inv[up+10];
int f[N],l,W[N],ans,tt,r[N],w[N];
int ksm(int x,int k)
{
    int s=1;for(;k;k>>=1,x=1ll*x*x%mod)
                if(k&1) s=1ll*s*x%mod;return s;
}
void NTT(int *P,int op)
{
    for(int i=0;i<l;i++) if(i<r[i]) swap(P[i],P[r[i]]);
    for(int i=1;i<l;i<<=1)
    {
        int W=ksm(3,(mod-1)/(i<<1));
        if(op<0) W=ksm(W,mod-2);w[0]=1;
        for(int j=1;j<i;j++) w[j]=1ll*w[j-1]*W%mod;
        for(int j=0,p=i<<1;j<l;j+=p)
            for(int k=0;k<i;k++)
            {
                int X=P[j+k],Y=1ll*P[j+k+i]*w[k]%mod;
                P[j+k]=(X+Y)%mod;P[j+k+i]=(mod+(X-Y)%mod)%mod;
            }
    }
    if(op<0) for(int inv=ksm(l,mod-2),i=0;i<l;i++) P[i]=1ll*P[i]*inv%mod;
}
int C(int n,int k) {return 1ll*jc[n]*inv[k]%mod*inv[n-k]%mod;}
int main()
{
    cin>>n>>m>>s;
    for(int i=0;i<=m;i++) cin>>W[i];
    jc[0]=inv[0]=1;
    for(int i=1;i<=up;i++) jc[i]=1ll*i*jc[i-1]%mod;
    inv[up]=ksm(jc[up],mod-2);
    for(int i=up-1;i>=1;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
    int lim=min(m,n/s);
    for(int i=0,bs=1;i<=lim;i++,bs=1ll*bs*inv[s]%mod)
        f[i]=1ll*C(m,i)%mod*C(n,i*s)%mod*bs%mod
            *jc[i*s]%mod*ksm(m-i,n-i*s)%mod;
    for(int i=0;i<=lim;i++)
        A[i]=1ll*jc[i]*f[i]%mod,B[i]=(i&1)?mod-inv[i]:inv[i];
    reverse(B+0,B+lim+1);
    for(l=1;l<=lim*2;l<<=1) tt++;tt--;
    for(int i=0;i<l;i++) r[i]=(r[i>>1]>>1)|((i&1)<<tt); 
    NTT(A,1);NTT(B,1);
    for(int i=0;i<l;i++) A[i]=1ll*A[i]*B[i]%mod;
    NTT(A,-1);
    for(int i=0;i<=lim;i++) (ans+=1ll*A[lim+i]*inv[i]%mod*W[i]%mod)%=mod;
    cout<<ans<<endl;
}

後記

orz dy&zjp_shadow!

題目質量至關高的!

當作模擬賽去打,結果是D1只有50+30+30,很涼了。

D2因爲作過,因此僞裝AK了。。

這樣的話和stdcall分數同樣了

!!可是:D2沒作過呢?D1時間只給三個半小時呢?那不切T1還有戲嗎?涼了!

因此說HAOI2018這場我進不了隊。

相關文章
相關標籤/搜索