DP好題集選

2019年到了,馬上又是省賽區域賽的一年了,但是我的dp水平還是堪憂,所以在衆多網站(主要codeforce)蒐羅一下dp的好題刷一下,並在此發一下解題報告來督促自己....

目錄

  1. MemSQL Start[c]UP 3.0 - Round 1 C.Pie Rules
  2. Avito Cool Challenge 2018 C. Colorful Bricks(組合數學+dp)
  3. Codeforces Round #538 (Div. 2) D. Flood Fill
  4. AtCoder Beginner Contest 118 D - Match Matching
  5. Codeforces Round #527 (Div. 3) F. Tree with Maximum Cost(樹上dp)
  6. Codeforces Round #518 (Div. 1) [Thanks, Mail.Ru!] A. Array Without Local Maximums(計數dp)
  7. [NOIP2018模擬賽] 小P的技能(計數dp+構造二叉樹)
  8. Codeforces Global Round 1 D. Jongmah(思維+巧妙DP)
  9. Hello 2019 D. Makoto and a Blackboard(概率dp+積性函數)
  10. Comparing Your Heroes ZOJ 2002(狀壓dp+拓撲思維)
  11. Codeforces Round #427 (Div. 2) D. Palindromic characteristics(區間dp+hash)
  12. Misunderstood … Missing 2018 Ec-final 西安(二維01揹包)
  13. Educational Codeforces Round 60 (Rated for Div. 2) D. Magic Gems(dp+矩陣快速冪優化)
  14. Codeforces Round #548 (Div. 2) D.Steps to On(期望DP+莫比烏斯反演)
  15. 浙江師範大學校賽 G.Little Sub and Piggybank(動態規劃)
  16. 山東省第十屆省賽 B.Flipping Game(DP)
  17. Educational Codeforces Round 64 (Rated for Div. 2)(換根dp)
  18. Atcoder AGC 030D Inversion Sum(期望dp)(好題)

MemSQL Start[c]UP 3.0 - Round 1 C.Pie Rules

題意:有n個派,每個派有一個大小,Alice和Bob有一個令牌,誰有令牌可以選擇當前派給自己或對方,同時下一回合沒有得到派的人將得到令牌,已知每個人都會選擇最優的策略,Alice和Bob最終得到的派的體積分別是多少?

題解:因爲第一回合是Bob擁有令牌,我們設dp[i]代表第i回合選擇的人的最大價值,那麼dp[1]就是Bob的答案,然後逆推進行轉移就可以了,很不錯的dp思維題~~

#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e6+7;
typedef long long ll;
const int mod=1e9+7;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/

int a[maxn],dp[maxn],sum[maxn];
int Sheryang(){

    int n=read;
    for(int i=1;i<=n;i++){
        a[i]=read;
    }
    for(int i=n;i>=1;i--){
        sum[i]=sum[i+1]+a[i];
        dp[i]=max(dp[i+1],sum[i+1]-dp[i+1]+a[i]);
    }

    printf("%d %d\n",sum[1]-dp[1],dp[1]);
    return 0;
}

Avito Cool Challenge 2018 C. Colorful Bricks(組合數學+dp)

題意:n個方格m種顏色,要求有k個方格與左邊的方格顏色不一樣的方案數。

題解:比較簡單的計數dp,dp[i][j]爲前i個方格有j種不同的方案數,枚舉到第i位的時候只有兩種狀態,和左邊一樣dp[i][j]+=dp[i-1][j]和左邊不一樣dp[i][j]+=dp[i-1][j-1]*(m-1)即可~~~

#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e6+7;
typedef long long ll;
const int mod=998244353;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/

ll dp[2005][2005];
void add(ll &x,ll y){
    x+=y;
    if(x>=mod) x-=mod;
}

int Sheryang(){
    int n=read,m=read,K=read;

    dp[1][0]=m;
    for(int i=2;i<=n;i++){
        for(int j=0;j<=min(K,i-1);j++){
            add(dp[i][j],dp[i-1][j]);
            if(j) add(dp[i][j],dp[i-1][j-1]*(m-1)%mod);
            //printf("%d %d = %lld\n",i,j,dp[i][j]);
        }
    }

    printf("%lld\n",dp[n][K]);
    return 0;
}

Codeforces Round #538 (Div. 2) D. Flood Fill

題意:相鄰方塊顏色顏色一樣則可成爲一個聯通塊,每次可以把一個聯通塊變爲相鄰聯通塊的顏色,最小變換多少次使得所有方塊都具有同一顏色。

題解:剛開始的方塊左右兩邊可能和自己是同一顏色,沒法直接進行區間dp,所以首先要將相同顏色的聯通塊壓成一塊,然後直接進行經典的區間dp就可以了~~(想到壓縮也想到區間dp,但是沒想到壓縮然後區間dp,卡了一段時間才過,蠢死~)

#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e6+7;
typedef long long ll;
const int mod=1e9+7;
#define pi acos(-1)
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/* keep hungry and keep calm! */

int a[maxn],c[maxn],n,dp[5005][5005];

int dfs(int l,int r){
    if(dp[l][r]!=-1) return dp[l][r];
    int ans=0;
    if(l==r) return 0;
    if(a[l]==a[r]) return ans=dfs(l+1,r-1)+1;
    else ans=min(dfs(l,r-1),dfs(l+1,r))+1;
    dp[l][r]=ans;return ans;
}

int Sheryang(){

    n=read;
    for(int i=1;i<=n;i++){
        c[i]=read;
    }

    a[1]=c[1];
    int cnt=1;

    for(int i=2;i<=n;i++){
        if(a[cnt]==c[i]) continue;
        a[++cnt]=c[i];
    }

    memset(dp,-1,sizeof(dp));
    printf("%d\n",dfs(1,cnt));

    return 0;
}
  • AtCoder Beginner Contest 118 D - Match Matching

  • 題意:有n根火柴,m種數字,數字1,2,3,4,5,6,7,8,9分別需要2,5,5,4,5,6,3,7,6根火柴,要求n根火柴全部都用完且拼成的數字最大,輸出這個數字。
  • 題解:簡單的完全揹包問題,先dp得出拼成的數字的最大位數然後將dp倒回去查找路徑,查找路徑的過程中可以貪心從最大的數字開始考慮。
  • Noting:dp數組需要一開始初始化爲-1,否則會dp出一個比答案大的值!!!
#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e6+7;
typedef long long ll;
const int mod=998244353;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/

int s[]={0,2,5,5,4,5,6,3,7,6},a[maxn],ans[maxn],n,m,dp[maxn];
int cmp(int a,int b){
    if(s[a]!=s[b]) return s[a]<s[b];
    return a>b;
}

int cmp1(int a,int b){
    return a>b;
}

int Sheryang(){

    n=read,m=read;
    for(int i=0;i<m;i++){
        a[i]=read;
    }
    memset(dp,-1,sizeof(dp));
    dp[0]=1;

    int vis[10]={0};
    for(int i=0;i<m;i++){
        for(int j=s[a[i]];j<=n;j++){
            dp[j]=max(dp[j],dp[j-s[a[i]]]+1);
        }
    }

    sort(a,a+m,cmp1);
    while(n){
        for(int i=0;i<m;i++){
            if(n>=s[a[i]] && dp[n-s[a[i]]]+1==dp[n]){
                putchar(a[i]+'0');
                n=n-s[a[i]];break;
            }
        }
    }

    puts("");
    return 0;
}

 

Codeforces Round #527 (Div. 3) F. Tree with Maximum Cost(樹上dp)

題意:給出一顆有n個節點樹以及n個節點的權值,定義一顆以rt爲根的樹的價值爲每一個節點到根的路徑長度*節點的權值,你可以以任意節點爲根,求樹的最大價值.

題解:首先我們先以節點1爲根,求出dp[i],表示i節點的子樹到i的路徑長度*權值,維護一個樹上的權值後綴和數組即可轉移,然後再進行一次dfs,考慮從u-v 即以u爲根的樹的價值如何轉移到以v爲根的樹的價值,首先v節點的子樹深度都會減少一層,然後u除v的子樹以外的子樹深度都增加一層,那麼轉移即可(感覺很清晰 經典的樹上dp呀)

#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e6+7;
typedef long long ll;
const int mod=998244353;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/

int a[maxn];
ll dp[maxn],sum[maxn];
vector<int>G[maxn];

void dfs1(int u,int fa){
    sum[u]=a[u];
    int sz=G[u].size();
    for(int i=0;i<sz;i++){
        int v=G[u][i];
        if(v==fa) continue;
        dfs1(v,u);
        sum[u]+=sum[v];
        dp[u]+=dp[v]+sum[v];
    }
}

void dfs2(int u,int fa){
    int sz=G[u].size();
    for(int i=0;i<sz;i++){
        int v=G[u][i];
        if(v==fa) continue;
        dp[v]=dp[u]-sum[v]+(sum[1]-sum[v]);
        dfs2(v,u);
    }
}

int Sheryang(){

    int n=read;
    for(int i=1;i<=n;i++){
        a[i]=read;
    }

    for(int i=0;i<n-1;i++){
        int u=read,v=read;
        G[u].push_back(v);
        G[v].push_back(u);
    }

    dfs1(1,0);
    dfs2(1,0);

    printf("%lld\n",*max_element(dp+1,dp+1+n));
    return 0;
}

Codeforces Round #518 (Div. 1) [Thanks, Mail.Ru!] A. Array Without Local Maximums(計數dp)

題意:給出一個數組,其中有幾項爲-1代表沒有數字,你可以在裏面填數字(1-200),要求滿足a[1]<=a[2]  && a[i]<=max(a[i-1],a[i+1]) && a[n]>=a[n-1] 的方案數

題解:典型的計數dp,對於第i位你只需要枚舉這一位選什麼數字以及這一位與i-1位的關係即可,dp[i][j][k]代表第i位選j且與i-1位的關係k的方案數。

#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e5+7;
typedef long long ll;
const int mod=998244353;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/

ll dp[maxn][205][3],a[maxn]; /// a[i-1]與a[i] < = >
int Sheryang(){

    ll n=read;
    for(int i=1;i<=n;i++){
        a[i]=read;
    }

    for(int i=1;i<=200;i++){
        if(a[1]==-1 || a[1]==i){
            dp[1][i][0]=1;
        }
    }

    for(int i=2;i<=n;i++){
        ll tmp=0; /// a[i-1]<a[i]
        for(int j=1;j<=200;j++){
            if(a[i]==j || a[i]==-1){
                dp[i][j][0]=tmp;
                dp[i][j][1]=dp[i-1][j][0]+dp[i-1][j][1]+dp[i-1][j][2]; ///a[i]==a[i-1]
            }
            tmp+=dp[i-1][j][0]+dp[i-1][j][1]+dp[i-1][j][2];
            tmp%=mod;
        }

        tmp=0;/// a[i-1]>a[i]
        for(int j=200;j>=1;j--){
            if(a[i]==j || a[i]==-1){
                dp[i][j][2]=tmp;
            }
            tmp+=dp[i-1][j][1]+dp[i-1][j][2];
            tmp%=mod;
        }
    }

    ll ans=0;
    for(int i=1;i<=200;i++){
        ans+=dp[n][i][1]+dp[n][i][2];
        ans%=mod;
    }
    cout<<ans<<endl;
    return 0;
}

[NOIP2018模擬賽] 小P的技能(計數dp+構造二叉樹)

題意:一顆無限大的二叉樹,選擇n個節點,要求選擇一個節點必先選擇父節點,問能選擇到第k層節點的方案數。

題解:神仙dp啊!!!一開始的思路dp[i][j]選擇j個節點能選到第i層的方案數,但是太難了....實在是不會,看過標稱後發現,正解的dp是dp[i][j]選擇j個節點不能選到第j層的方案數....經過Tangjz老師的指點,dp[i][j]意思是選擇j個節點深度小於i的二叉樹的數量,考慮一顆經過根節點的二叉樹,根節點左側樹的大小爲u,根節點右側樹的大小爲v,且層數都小於i,那麼刪掉根節點後就可以得到兩個使用節點分別是u和v且深度小於i-1的二叉樹,兩顆子樹相互不影響那麼dp[i][u+v+1]就可以這麼轉移過來.....

#include<stdio.h>
#include<math.h>
#include<algorithm>
#include<string.h>
#include<stdlib.h>
#include<ctype.h>
using namespace std;
#define Sheryang main
const int maxn=2e5+7;
typedef long long ll;
const int mod=998244353;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/

ll f[505][505]; ///f[i][j] 層數小於i點數爲j的二叉樹的個數
int Sheryang(){
    int n=read,k=read;

    for(int i=1;i<=n+1;i++){
        f[i][0]=1;
    }
    for(int i=2;i<=n+1;i++){
        for(int j=1;j<=n;j++){
            for(int k=0;k<j;k++){
                f[i][j]+=f[i-1][k]*f[i-1][j-k-1];
                f[i][j]%=mod;
            }
        }
    }

    ll Ans=(f[n+1][n]-f[k][n]+mod)%mod;
    printf("%lld\n",Ans);
    return 0;
}

 

Codeforces Global Round 1 D. Jongmah(思維+巧妙DP)

題意:給出n個在m以內數字,每個數字可以構成[i,i,i] [i-1,i,i+1]的三元組,每個數只能組成一個三元組,問最優組成多少三元組?

題解:這個題有點不太好想,轉移什麼的真的很巧妙,或許這就是dp的真諦吧....

首先有一個結論,對於一個數x的遞增三元組不會超過二個,否則可以轉換成三個三張牌相同的三元組,答案是同樣優的,對於這個dp我們可以設計三個狀態dp[i][j][k]表明考慮數字i時,存在j個以i開頭的遞增三元組,k個以i做爲中間元素的遞增三元組.那麼便開始考慮從第dp[i-1][k][l] -> dp[i][j][k] 只需要在後面補上幾個數字便可以得到轉移,同時數字i用了j+k+l個,剩下的可以全部變成[i,i,i]的三元組

#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e6+7;
typedef long long ll;
const int mod=998244353;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/

int f[maxn][3][3],cnt[maxn];
int Sheryang(){
    int n=read,m=read;
    memset(f,-1,sizeof(f));

    for(int i=1;i<=n;i++){
        int x=read;
        cnt[x]++;
    }

    f[0][0][0]=0;
    for(int i=1;i<=m;i++){
        for(int j=0;j<3;j++){
            for(int k=0;k<3;k++){
                for(int l=0;l<3;l++){
                    if(cnt[i]>=j+k+l){
                        f[i][j][k]=max(f[i][j][k],f[i-1][k][l]+j+(cnt[i]-j-k-l)/3);
                    }
                }
            }
        }
    }

    printf("%d\n",f[m][0][0]);
    return 0;
}

Hello 2019 D. Makoto and a Blackboard(概率dp+積性函數)

題意:開始你手裏有一個數字n,每一回合它都會等概率的變成它的一個因子,問k回合後這個數字大小的期望.

題解:首先對於這個期望可以猜一下,這是個積性函數....(爲啥?我也不知道...要是有大佬知道可以告訴我一下),那麼對於積性函數,總期望=每個質因子的的期望的乘積,那麼對於每個質因子進行概率dp,dp[i][j]對於某一個質因子來說第i輪時冪次爲j的概率是多少,可以枚舉第i-1輪的時候的冪次k,轉移的概率是1/(k+1),最後再根據概率處理出期望即可.....

#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e5+7;
typedef long long ll;
const int mod=1e9+7;
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
 
/* keep hungry and keep calm! */
ll dp[(int)1e4+7][62],n,k,inv[62];
 
ll qpow(ll a,ll b){
    ll ans=1;a%=mod;
    while(b){
        if(b&1) ans=(ans*a)%mod;
        a=(a*a)%mod;b/=2;
    }
    return ans;
}
 
ll solve(ll p,int e){
    dp[0][e]=1;
    for(int i=0;i<e;i++) dp[0][i]=0;
 
    for(ll i=1;i<=k;i++){
        for(int j=0;j<=e;j++){
            dp[i][j]=0;
            for(int k=j;k<=e;k++){
                dp[i][j]=(dp[i][j]+dp[i-1][k]*inv[k+1]%mod)%mod;
            }
        }
    }
    ll tmp=0,t=1;
    for(int i=0;i<=e;i++){
        tmp=(tmp+t*dp[k][i]%mod)%mod;
        t=(t*p)%mod;
    }
    return tmp;
}
 
int Sheryang()
{
    inv[1]=1;
    for(int i=2;i<=60;i++){
        inv[i]=qpow(i,mod-2);
    }
 
    n=read,k=read;
    ll ans=1;
 
    for(ll i=2;i*i<=n;i++){
        int cot=0;
        while(n%i==0) cot++,n/=i;
        if(cot) ans=(ans*solve(i,cot)%mod)%mod;
    }
    if(n>1) ans=(ans*solve(n,1)%mod)%mod;
 
    printf("%lld\n",ans);
    return 0;
}

Comparing Your Heroes ZOJ 2002(狀壓dp+拓撲思維)

題意:給你n對拳皇的名字,代表誰比誰強,要求你根據這個給出現的人物排序,一共有多少種排序的方法?

題解:首先應該能想到一種計數dp,但就沒有更深入的想法了....但是我們可以首先將給我們的點對進行連邊,這樣就變成了一個有向圖,我們可以想從前往後進行安排,首先可以安排哪些人?應該是哪些沒有入度的點,因爲沒有人比他們強,那我們延伸一下,若存在一條邊u->v,則u一定在v之前安排,那麼對於一個點v,只要它的前綴點全部出現在一個排列中,那麼就可以安排它,處理這種排練的是狀壓dp.

#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e6+7;
typedef long long ll;
const int mod=1e9+7;
///#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
///char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/

int dp[maxn],pre[maxn],cnt;
char s[maxn];


int Sheryang()
{
    int n;
    while(~scanf("%d",&n)){
        memset(dp,0,sizeof(dp));
        memset(pre,0,sizeof(pre));
        map<string,int>mp;
        cnt=0;
        for(int i=1;i<=n;i++){
            scanf("%s",s);
            int u,v;
            if(!mp.count(s)){
                mp[s]=cnt++;
            }
            u=mp[s];

            scanf("%s",s);
            if(!mp.count(s)){
                mp[s]=cnt++;
            }
            v=mp[s];
            pre[v]|=1<<u;
        }

        dp[0]=1;
        for(int i=0;i<(1<<cnt);i++){if(dp[i])
            for(int j=0;j<cnt;j++){
                if(!(i&(1<<j)) && (i&pre[j])==pre[j]){
                    dp[i|(1<<j)]+=dp[i];
                }
            }
        }

        printf("%d\n",dp[(1<<cnt)-1]);
    }
    return 0;
}

 

Codeforces Round #427 (Div. 2) D. Palindromic characteristics(區間dp+hash)

題意:給你一個字符串,定義k-迴文串的含義是對於長度爲n的迴文字符串,左邊長度n/2的子串和右邊長度n/2的子串是(k-1)-迴文串,輸出分別有多少個k-迴文串

題解:很容易可以想到一種區間dp的做法,因爲對於一個區間只有一個轉移,所以總體複雜度n*n,但是分出區間之後還要判斷左右兩半翻轉過來是否相等,這個可以用hash來判斷,求一個前綴hash和一個後綴hash,若是迴文串,則dp[i][j]表示從i到j最大能形成編號爲多少的迴文串,dp[i][j]=min(dp[i][i+len/2-1],dp[j-len/2+1][j])+1,轉移即可,但是這個題卡hash函數,以後謹慎一點用雙hash...

#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=1e6+7;
typedef long long ll;
const int mod=1e9+7;
void Smax(ll &a,ll b){if(a<b) a=b;}
void Smin(ll &a,ll b){if(a>b) a=b;}
///#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
///char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/

char s[maxn];
int dp[5002][5002],ans[5005];
ll Has[maxn],p=2333333,Pow[maxn],Hass[maxn];

int judge(int l1,int r1,int l2,int r2){
    ll t1=(Has[r1]-(Has[l1-1]*Pow[r1-l1+1]%mod)+mod)%mod;
    ll t2=(Hass[l2]-(Hass[r2+1]*Pow[r2-l2+1]%mod)+mod)%mod;
    return t1==t2;
}

int Sheryang()
{
    scanf("%s",s+1);
    int n=strlen(s+1);

    Pow[0]=1;
    for(int i=1;i<=n;i++){
        dp[i][i]=1;
        Has[i]=(Has[i-1]*p+s[i])%mod;
        Pow[i]=(Pow[i-1]*p)%mod;
    }
    for(int i=n;i>=1;i--){
        Hass[i]=(Hass[i+1]*p+s[i])%mod;
    }
    ans[1]=n;

    for(int len=2;len<=n;len++){
        for(int i=1;i+len-1<=n;i++){
            int j=i+len-1;
            if(judge(i,i+len/2-1,j-len/2+1,j)){
                dp[i][j]=min(dp[i][i+len/2-1],dp[j-len/2+1][j])+1;
                ans[dp[i][j]]++;
            }
        }
    }

    for(int i=n-1;i>=1;i--){
        ans[i]+=ans[i+1];
    }
    for(int i=1;i<=n;i++){
        printf("%d ",ans[i]);
    }
    return 0;
}

 

Misunderstood … Missing 2018 Ec-final 西安(二維01揹包)

題意:初始玩傢俱有攻擊力A和攻擊力增幅D都爲0,每一回合有三種操作,1.直接對敵方造成A+a[i]點傷害,2.將攻擊力增幅提升b[i] 3.將攻擊力提升c[i] 回合結束時,攻擊力提升當前的攻擊力增幅,求n回合後對敵方造成的最大傷害。

題解:可以想到逆推,若最後一回合,那麼一定要選擇第一個操作,那麼若是第i回合,我們討論第二種操作和第三種操作對於答案的影響,對於第三種種操作,對於傷害會提升後面的攻擊次數*c[i],那麼我們dp狀態要加上後面的攻擊次數,第二種狀態,這個還要設計到後面攻擊發動的位置,那麼我們設後面j次攻擊的位置分別爲k1,k2...kj,設k=k1+k2+...+kj,那麼對於答案的影響是(k1-i+k2-i+...+kj-i)*b[i]=(k-j*i)*c[i],然後還要滾動數組優化一下,同時狀態的枚舉要參照揹包那樣,倒着枚舉。

#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=2e5+7;
typedef long long ll;
#define TYPE ll
const int mod=1e9+7;
void Smax(TYPE &a,TYPE b){if(a<b) a=b;}
void Smin(TYPE &a,TYPE b){if(a>b) a=b;}
///#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
//ar buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<TYPE,TYPE>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
 
ll a[105],b[105],c[105],dp[2][102][5051];
int Sheryang(){
 
    int T=read;
    while(T--){
 
        memset(dp,0,sizeof(dp));
        int n=read;
        for(int i=1;i<=n;i++){
            a[i]=read;
            b[i]=read;
            c[i]=read;
        }
        dp[n&1][1][n]=a[n];
 
        ll ans=0;
        for(int i=n-1;i>=1;i--){
            for(int j=n-i;j>=1;j--){
                int up=(2*n-j+1)*j/2,down=(2*i+j-1)*j/2;
                for(int k=up;k>=down;k--){
                    int now=i&1,pre=(i+1)&1;
                    Smax(dp[now][j+1][k+i],dp[pre][j][k]+a[i]); //第i次攻擊
                    Smax(dp[now][j][k],dp[pre][j][k]+j*c[i]);
                    Smax(dp[now][j][k],dp[pre][j][k]+(k-i*j)*b[i]);
 
                    Smax(ans,dp[now][j+1][k+i]);
                    Smax(ans,dp[now][j][k]);
                }
            }
 
        }
 
        printf("%lld\n",ans);
   }
    return 0;
}

 

Educational Codeforces Round 60 (Rated for Div. 2) D. Magic Gems(dp+矩陣快速冪優化)

題意:有n個空間,有兩種數字1和0,他們分別都能佔據1個空間,初始你有任意個數字1,數字1可以轉化成m個數字0,求佔滿n個空間的方案數.

題解:狀態轉移方程其實挺好寫的dp[i]代表佔據i空間的方案數,dp[i]=dp[i-1]+dp[i-m]佔據i個空間可以是佔據i-1空間和佔據i-m空間枚舉過來的,當i<m時,dp[i]=1,但是n特別大1e18,所以考慮優化這個遞推式,可以用矩陣快速冪:初始矩陣便是m行1列,dpn]一直到dp[n-m+1],

然後根據上一項的寫轉移矩陣就可以了。

#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
#define TYPE int
const int maxn=1e6+7;
typedef long long ll;
const int mod=1e9+7;
void Smax(TYPE &a,TYPE b){if(a<b) a=b;}
void Smin(TYPE &a,TYPE b){if(a>b) a=b;}
///#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
///char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/

struct mat{
    ll m[105][105];
};

int m;
const mat operator *(mat &a,mat &b){
    mat ans;
    for(int i=1;i<=m;i++){
        for(int j=1;j<=m;j++){
            ans.m[i][j]=0;
            for(int k=1;k<=m;k++){
                ans.m[i][j]+=a.m[i][k]*b.m[k][j];
                if(ans.m[i][j]>=mod) ans.m[i][j]%=mod;
            }
        }
    }
    return ans;
}

ll qpow(mat a,ll b){
    mat ans;
    for(int i=1;i<=m;i++){
        for(int j=1;j<=m;j++){
            ans.m[i][j]=(i==j);
        }
    }

    while(b){
        if(b&1) ans=ans*a;
        a=(a*a);b>>=1;
    }

    ll res=ans.m[1][1];
    for(int i=1;i<=m;i++){
        res+=ans.m[1][i];
        if(res>=mod) res-=mod;
    }
    return res;
}


int Sheryang(){

    ll n=read;
    m=read;

    if(n<m){
        return printf("1"),0;
    }else if(n==m){
        return printf("2"),0;
    }

    mat a;
    memset(a.m,0,sizeof(a.m));
    a.m[1][1]=1;a.m[1][m]=1;
    for(int i=2;i<=m;i++){
        a.m[i][i-1]=1;
    }

    printf("%lld\n",qpow(a,n-m));
    return 0;
}

 

Codeforces Round #548 (Div. 2) D.Steps to On(期望DP+莫比烏斯反演)

題意:有一個爲空的隊列,每次往隊列中隨機添加1-m中的一個數,然後求出隊列的gcd,若gcd==1則終止,否則繼續添加,求最終的隊列期望長度。

題解:首先我們可以得到一個期望dp的式子來f(i)=1+\frac{\sum_{j=1}^{j=m}f(gcd(i,j)) }{b} 但是這樣的複雜度是O(m^{2}) ,複雜度還是不足以過掉這道題,所以我們需要進行優化:

 設F(n,x)代表1-m中有多少個i使得 gcd(x,i)=n 成立,則 f(i)=1+ \frac{ \sum_{d|i}f(d)*F(d,i) }{m} 如此,問題便變成了如何解決F(n,x)? 易知 F(n,x)= \sum_{i=1}^{m} [gcd(i,x)==n] 如此我們便構造一個新函數G(n,x)=\sum_{n|d}F(d,x)

G(n,x)=\sum_{i=1}^{m}\sum_{n|d}[gcd(i,x)==d]\\ =\sum_{i=1}^{m} [n|gcd(i,x)] \\= \sum_{i=1}^{m/n}[1|gcd(i,x/n)] \\=m/n*[n|x],如此根據莫比烏斯反演的公式 F(n)=\sum_{n|d}f(d) \Rightarrow f(n)=\sum_{n|d} \mu(d/n) F(d) 

 

F(n,x)=\sum_{n|d}\mu(d/n)G(d,x) \\=\sum_{i=1}^{m/n}\mu(i)G(n*i) \\=\sum_{i=1}^{m/n}\mu(i)m/(n*i)[(n*i)|x] \\\sum_{i|(n/x)}\mu(i)m/(n*i) 代入即可,複雜度O(mlg^2(m))

 

 

#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
#define TYPE int
const int maxn=1e6+7;
typedef long long ll;
const int mod=1e9+7;
void Smax(TYPE &a,TYPE b){if(a<b) a=b;}
void Smin(TYPE &a,TYPE b){if(a>b) a=b;}
///#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
///char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/

vector<int>v[maxn];
int pri[maxn],vis[maxn],mu[maxn],n;
ll f[maxn];
void primeall(){
	int cot=0;
	for(int i=2;i<maxn;i++){
		if(vis[i]==0){
			pri[cot++]=i;
			mu[i]=-1;
		}
		for(int j=0;j<cot&&i*pri[j]<maxn;j++){
			vis[i*pri[j]]=1;
			if(i%pri[j]==0){
				break;
			}
			mu[i*pri[j]]=-mu[i];
		}
	}
}

int calc(int x,int y){ /// 1-n gcd(x,i)==y
	int g=x/y,ans=0;
	for(auto p:v[g]){
		ans+=mu[p]*(n/p/y);
	}
	return ans;
}

ll qpow(ll a,ll b){
	ll ans=1;
	while(b){
		if(b&1) ans=(ans*a)%mod;
		a=(a*a)%mod;b/=2;
	}
	return ans;
}

int Sheryang(){
	primeall();
	n=read;
	
	for(int i=1;i<=n;i++){
		for(int j=i;j<=n;j+=i){
			v[j].push_back(i);
		}
	}
	
	f[1]=1;mu[1]=1;
	ll inv=qpow(n,mod-2),ans=1;
	for(int i=2;i<=n;i++){
		for(int j=0;j<v[i].size()-1;j++){
			int d=v[i][j];
			f[i]=(f[i]+f[d]*calc(i,d)%mod)%mod;
		}
		f[i]=(f[i]*inv+1)%mod;
		f[i]=f[i]*n%mod*qpow(n-calc(i,i),mod-2)%mod;
		ans+=f[i];ans%=mod;
	}
	
	printf("%lld\n",ans*inv%mod);
    return 0;
}

浙江師範大學校賽 G.Little Sub and Piggybank(動態規劃)

題意:有一個神奇的存錢罐,每天可以往裏面存任意實數的錢,但是後一天存的錢不能比前一天存的錢多,每天都有價值爲v的商品,只有當存錢罐裏的錢等於商品的價值纔可以購買,購買後將得到快樂值,求最大的快樂值。

題解:可以設計dp[i][j]代表最後買的商品是i倒數第二件買的商品是j所能得到的最大快樂值,那麼狀態轉移便是dp[i][j] = max(dp[j][k]) + v[i]; 如何直接轉移複雜度是O(n^3) 但是這個max(dp[j][k])其實是可以在之前的轉移中去維護的,定義mx[i][j] = max(dp[i][j]~dp[i][i]) ,則狀態轉移便可以改寫成 dp[i][j] = mx[j][k] + v[i] k是任意的嗎?肯定不是的,我們便要找到最小的k,又因爲之前的條件,後一天存的錢不能比前一天多,\frac{w[i]]}{i-j} \leq \frac{w[j]}{j-k} 於是化簡得到k \geq j-\frac{w[j]}{w[i]}*(i-j)

#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=2e5+7;
typedef long long ll;
const int mod=1e9+7;
///#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
///char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/
  
int w[maxn],v[maxn],dp[2005][2005],mx[2005][2005];

int Sheryang(){
    
    int n=read;
    
    for(int i=1;i<=n;i++){
    	w[i] = read;
	}
	for(int i=1;i<=n;i++){
		v[i] = read;
	}
	
	// mx[i][j]  dp[i][j] - dp[i][i-1]的最大值 
	// dp[i][j] = max(dp[j][k]) + v[i] 
	for(int i=1;i<=n;i++){
		
		dp[i][0] = v[i];
		for(int j=1;j<i;j++){
			int k = (w[i]==0)? 0:max((int)ceil( j*1.0-1.0*w[j]/w[i]*(i-j) ),0);
			if(mx[j][k]) dp[i][j] = mx[j][k] + v[i];
		}
		
		for(int j=i-1;j>=0;j--){
			mx[i][j] = max(mx[i][j+1],dp[i][j]);
		}
	}
	
	int ans = 0;
	for(int i=1;i<=n;i++){
		ans = max(ans,mx[i][0]);
	}
	
	printf("%d\n",ans);
    return 0;
}

山東省第十屆省賽 B.Flipping Game(DP)

題意:給你長度爲n的01串的初始狀態和最終狀態,可改變m輪,每輪改變k個字符的狀態 0->1 1->0,輸出從初始狀態變到末尾狀態的方案數(n,m,j<=100).

題解:比賽的時候死活沒想到這個咋dp....狀態想不到,賽後看到別人代碼恍然大悟,其實是個蠻簡單的dp,dp[i][j]代表第i輪s和t有j個相同字符的方案數,如此狀態轉移便可以枚舉之前的狀態,然後枚舉將多少個1變成0,然後組合數計算一下便可以了.....

#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=5e4+7;
typedef long long ll;
const int mod=998244353;
///#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
///char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/


ll dp[105][105],C[105][105];
char s[105],t[105];
int Sheryang(){
	C[0][0] = 1;
    for(int i=0;i<=100;i++){ 
    	for(int j=0;j<=i;j++){
    		if(i==j || !j){
    			C[i][j] = 1;
			}else{
				C[i][j] = C[i-1][j] + C[i-1][j-1];
			}
    		if(C[i][j]>=mod){
    			C[i][j] -= mod;
			}
		}
	}
	int T = read;
	while(T--){
		int n=read,m=read,K=read;
		
		for(int i=0;i<=m;i++){
			for(int j=0;j<=n;j++){
				dp[i][j] = 0;
			}
		}
		
		scanf("%s%s",s+1,t+1);
		
		int num = 0;
		for(int i=1;s[i];i++){
			if(s[i] == t[i]){
				num ++;
			}
		}
		dp[0][num] = 1; //初始有num位相同 
		
		for(int i=0;i<m;i++){
			for(int j=0;j<=n;j++){ //枚舉有多少個 0 
				if(dp[i][j]){
					int limit = min(j,K);
					for(int k=0;k<=limit;k++){ // k位相同的翻成不同的 K-k位不同翻成相同 
						dp[i+1][j+(K-k)-k] += dp[i][j]*C[j][k]%mod*C[n-j][K-k]%mod;
						dp[i+1][j+(K-k)-k] %= mod;
					}
				}
			}
		}
		
		printf("%lld\n",dp[m][n]);
	}
    return 0;
}

Educational Codeforces Round 64 (Rated for Div. 2)(換根dp)

題意:給你一顆n個節點的樹,邊權只有0和1,在樹上選擇兩個節點u,v,若u到v的邊權是不遞減的,則是合法的路徑,求一共有多少的合法路徑?

題解:dp[u][0/1/2/3/4]從u到底,全0 先0後1 先1後0 全1的路徑有多少,然後就可以枚舉一條邊進行換根dp,狀態轉移有點多,需要仔細考慮一下所有的可行狀態,同時若u->v路徑上的權值是不變的,那麼答案就要*2,同時注意一下狀態是從上到下的就可以了......

#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=2e5+7;
typedef long long ll;
const int mod=1e9+7;
//#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
//char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/

int nxt[maxn<<2],head[maxn],to[maxn<<2],w[maxn<<2],cnt;
void add(int u,int v,int vol){
	nxt[++cnt] = head[u];
	head[u] = cnt;
	to[cnt] = v;
	w[cnt] = vol;
}
ll dp[maxn][5],Ans;
// 00 01 10 11

void dfs(int u,int fa){
	ll tmp = 0;
	for(int i=head[u];~i;i=nxt[i]){
		int v = to[i];
		int vol = w[i];
		if(v == fa){
			continue;
		}
		dfs(v,u);
		if(vol){
			tmp += dp[u][0]*(dp[v][3]+1);
			tmp += dp[u][2]*(dp[v][3]+1);
			tmp += dp[u][3]*(dp[v][0]+dp[v][2]+2*dp[v][3]+2);
			tmp += dp[v][0]+dp[v][2]+2*dp[v][3]+2;
			
			dp[u][3] += dp[v][3] + 1;
			dp[u][2] += dp[v][0] + dp[v][2];
		}else{
			tmp += dp[u][0]*(2*dp[v][0] + dp[v][1] + dp[v][3] + 2);
			tmp += dp[u][1]*(dp[v][0]+1);
			tmp += dp[u][3]*(dp[v][0]+1);
			tmp += 2*dp[v][0] + dp[v][1] + dp[v][3]+2;
			
			dp[u][0] += dp[v][0]+1;
			dp[u][1] += dp[v][1] + dp[v][3];
		}
	}
	Ans += tmp;
}

int Sheryang(){
      
   	int n = read;
	memset(head,-1,sizeof head);
	
	for(int i=1;i<n;i++){
		int u = read, v = read,vol = read;
		add(u,v,vol);
		add(v,u,vol);
	}
	
	dfs(1,0);
	
	printf("%lld\n",Ans);
    return 0;
}

Atcoder AGC 030D Inversion Sum(期望dp)(好題)

題意:有一個序列a1,a2,a3....an,有Q次操作,每次給出x和y,可以選擇是否交換x和y位置上的數字,共有2^Q種可能,計算所有可能的逆序數的和。n,Q <= 3000

題解:dp[i][j][k] 代表前i輪a[j]<a[k]的期望次數,若這輪選擇交換 dp[i][x][y] += dp[i-1][y][x] 不交換的話dp[i][x][y] += dp[i-1][x][y] 其他n個位置同理,最後乘上總方案數就可以了。

#include<bits/stdc++.h>
using namespace std;
#define Sheryang main
const int maxn=2e5+7;
typedef long long ll;
const int mod=1e9+7;
///#define getchar()(p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
///char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
#define IO cin.tie(0),ios::sync_with_stdio(false);
#define pi acos(-1)
#define PII pair<ll,ll>
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
#define read read()
/** keep hungry and keep calm! **/

ll qpow(ll a,ll b){
	ll ans = 1;
	while(b){
		if(b&1) ans = ans*a%mod;
		a=a*a%mod; b/=2;
	}
	return ans;
}

ll dp[3005][3005];
int a[3005];
int Sheryang(){
    
	int n = read , Q = read;
	ll tot = qpow(2,Q);
	ll inv = qpow(2,mod-2);
	
	for(int i=1;i<=n;i++){
		a[i] = read;
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			dp[i][j] = (a[i]<a[j]);
		}
	}
	
	// dp[x][y] a[x]<a[y] 的期望 
	while(Q--){
		int x = read,y = read;
		dp[x][y] = dp[y][x] = (dp[x][y]+dp[y][x])*inv%mod;
		for(int i=1;i<=n;i++){
			if(i!=x&&i!=y){
				dp[i][x] = dp[i][y] = (dp[i][x]+dp[i][y])*inv%mod;
				dp[x][i] = dp[y][i] = (dp[x][i]+dp[y][i])*inv%mod;
			}
		}
	}
	
	ll ans = 0;
	for(int i=2;i<=n;i++){
		for(int j=1;j<i;j++){
			ans += dp[i][j];
			if(ans>=mod) ans -= mod;
		}
	}
	ans = ans*tot%mod;
	
	printf("%lld\n",ans);
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章