1.

标准的卢卡斯定理加数位dp,主要是算C(n,i)*C(n,2*i)。

但由于这题的模数是质数,就不需要考虑很多东西,如:是否超过上限了、是否有连续的进位。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long int ll;
 4 const int maxn=1E6+5;
 5 ll ans,n,mod;
 6 ll fac[maxn],inv[maxn];
 7 int tot,a[66];
 8 inline ll qpow(ll x,ll y)
 9 {
10     ll ans=1,base=x;
11     while(y)
12     {
13         if(y&1)
14             ans=ans*base%mod;
15         base=base*base%mod;
16         y>>=1;
17     }
18     return ans;
19 }
20 inline ll C(int x,int y)
21 {
22     if(x<y||x<0||y<0)
23         return 0;
24     return fac[x]*inv[y]%mod*inv[x-y]%mod;
25 }
26 inline void init()
27 {
28     fac[0]=1;
29     for(int i=1;i<mod;++i)
30         fac[i]=fac[i-1]*i%mod;
31     inv[mod-1]=qpow(fac[mod-1],mod-2);
32     for(int i=mod-2;i>=0;--i)
33         inv[i]=inv[i+1]*(i+1)%mod;
34     ll x=n;
35     do
36     {
37         a[++tot]=x%mod;
38         x/=mod;
39     }while(x);
40     reverse(a+1,a+tot+1);
41 }
42 inline ll solve1()
43 {
44     return (qpow(3,n)-1)*qpow(2,mod-2)%mod;
45 }
46 bool vis[28][2];
47 ll f[28][2];
48 ll dfs(int pos,int pre)// limit doesn't matter
49 {
50     if(vis[pos][pre])
51         return f[pos][pre];
52     vis[pos][pre]=1;
53     if(pos==tot)
54         return f[pos][pre]=1;
55     ll s=0;
56     if(pre==0)
57         for(int nxt=0;nxt<=1;++nxt)
58         {
59             int up=(mod-1-nxt)/2;
60             for(int i=0;i<=up;++i)
61                 if((2*i>=mod)==nxt)
62                     s=(s+dfs(pos+1,nxt)*C(a[pos+1],(2*i+nxt)%mod)%mod*C((2*i+nxt)%mod,i))%mod;// it is correct even if 2*i plus an offset is equal to mod
63         }
64     else
65         for(int nxt=0;nxt<=1;++nxt)
66         {
67             int up=(mod-1-nxt)/2;
68             for(int i=up+1;i<=mod-1;++i)
69                 if((2*i>=mod)==nxt)
70                     s=(s+dfs(pos+1,nxt)*C(a[pos+1],(2*i+nxt)%mod)%mod*C((2*i+nxt)%mod,i))%mod;
71         }
72     return f[pos][pre]=s;
73 }
74 inline ll solve2()
75 {
76     ll s=dfs(0,0)-1;
77     return s*qpow(2,mod-2)%mod;
78 //  for(int i=1;2*i<=n;++i)
79 //      s=(s+C(n,2*i)*C(2*i,i)%mod)%mod;
80 //  return s*qpow(2,mod-2)%mod;
81 }
82 int main()
83 {
84     ios::sync_with_stdio(false);
85     cin>>n>>mod;
86     init();
87     ll ans=(solve1()+solve2())%mod;
88     cout<<((qpow(3,n)-ans-1)%mod+mod)%mod<<endl;
89     return 0;
90 }
View Code

 

2.一个数列是好的,当且仅当它第一位是1,并且每次向后移动一位时,前缀max最多增加1。对于每个数字k,求出所有可能的好的数列中它的出现次数的平方的和。n<=5000。

考虑如何计算一对位置的贡献和单独位置的贡献。构造一个好的数列的过程,类似于在二维平面上要么向右移动一步,要么向右上移动一步。其中向右移动一步共有y坐标种数字,向右上移动一步就是钦定放了数字y+1,可以依次设计dp然后前缀和优化。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long int ll;
 4 const int maxn=5E3+5;
 5 int n,mod;
 6 int f[maxn][maxn],g[maxn][maxn][2];
 7 int sum1[maxn][maxn],sum0[maxn][maxn];
 8 int G[55];
 9 inline void write(int x)
10 {
11     int g=0;
12     do{G[++g]=x%10;x/=10;}while(x);
13     for(int i=g;i>=1;--i)putchar('0'+G[i]);putchar(' ');
14 }
15 inline void add(int&x,int y)
16 {
17     x=(x+y)%mod;
18 }
19 inline int get(int x)
20 {
21     int s=0,h=0;
22     for(int i=1;i<=n;++i)
23     {
24         add(s,sum1[i-1][x]);
25 //      for(int j=x;j<=n;++j)
26 //          add(s,(ll)f[i-1][j]*g[i][j][1]%mod*2%mod);
27         add(h,(ll)f[i-1][x-1]*g[i][x][1]%mod);
28         if(i==n)
29             break;
30         add(s,sum0[i-1][x]);
31 //      for(int j=x;j<=n;++j)
32 //          add(s,(ll)f[i-1][j]*g[i][j][0]%mod);
33         add(s,(ll)f[i-1][x-1]*g[i][x][0]%mod);
34     }
35     h=h*2%mod;
36     for(int i=x-1;i<=n;++i)
37         add(s,f[n-1][i]);
38     return (s+h)%mod;
39 }
40 inline void solve()
41 {
42     f[0][0]=1;
43     for(int i=1;i<=n;++i)
44         for(int j=1;j<=i;++j)
45             f[i][j]=(f[i-1][j-1]+(ll)f[i-1][j]*j%mod)%mod;
46     for(int i=1;i<=n;++i)
47         g[n][i][0]=1;
48     for(int i=n;i>1;--i)
49         for(int j=1;j<=i;++j)
50         {
51             add(g[i-1][j][0],(ll)g[i][j][0]*j%mod);
52             add(g[i-1][j][1],g[i][j][0]);
53             add(g[i-1][j][1],(ll)g[i][j][1]*j%mod);
54              
55             add(g[i-1][j-1][0],g[i][j][0]);
56             add(g[i-1][j-1][1],g[i][j][1]);
57         }
58     for(int i=1;i<=n;++i)
59         for(int j=i;j>=1;--j)
60             add(sum1[i][j],sum1[i][j+1]+(ll)f[i][j]*g[i+1][j][1]%mod*2%mod);
61     for(int i=1;i<=n;++i)
62         for(int j=i;j>=1;--j)
63             add(sum0[i-1][j],sum0[i-1][j+1]+(ll)f[i-1][j]*g[i][j][0]%mod);
64     for(int i=1;i<=n;++i)
65         write(get(i));
66 }
67 int main()
68 {
69     ios::sync_with_stdio(false);
70     cin>>n>>mod;
71     solve();
72     return 0;
73 }
View Code

3.斜率优化。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long int ll;
 4 typedef long double ld;
 5 const ld inf=1E100;
 6 int n,a[305],b[305];
 7 ll f[2][305][305];
 8 struct pt
 9 {
10     ld x,y;
11     int num;
12     pt(ld a=0,ld b=0,int c=0):x(a),y(b),num(c){}
13 };
14 inline ld slope(pt A,pt B)
15 {
16     return (A.y-B.y)/(A.x-B.x);
17 }
18 struct AF
19 {
20     pt p[305];
21     int head,tail;
22     AF()
23     {
24         head=1,tail=0;
25     }
26     void clear()
27     {
28         head=1,tail=0;
29     }
30     inline void add(pt A)
31     {
32         while(head<tail&&slope(p[tail-1],p[tail])>=slope(p[tail],A))
33             --tail;
34         p[++tail]=A;
35     }
36     inline int ask(ld k)
37     {
38         if(head>tail)
39             return -1;
40         while(head<tail&&slope(p[head],p[head+1])<k)
41             ++head;
42         return p[head].num;
43     }
44 }Q[305];
45 inline ll s(ll x)
46 {
47     return x*x;
48 }
49 inline void solve()
50 {
51     for(int i=0;i<2;++i)
52         for(int j=0;j<=300;++j)
53             for(int k=0;k<=300;++k)
54                 f[i][j][k]=(ll)1000000000000;
55     f[0][0][0]=0;
56     for(int i=1;i<=n;++i)
57     {
58         int p1=i&1,p0=(i&1)^1;
59         for(int j=0;j<=b[i];++j)
60         {
61             Q[j].clear();
62             for(int k=0;k<=b[i];++k)
63                 f[p1][j][k]=(ll)1000000000000;
64         }
65         if(i==1)
66             for(int k=1;k<=b[i];++k)
67                 f[p1][k][k]=0;
68         else
69             for(int k=1;k<=b[i];++k)
70                 for(int l=1;l<=b[i-1];++l)
71                     f[p1][k][k]=min(f[p1][k][k],f[p0][b[i-1]][l]+s(a[i]+k-a[i-1]-l));
72         for(int j=1;j<=b[i];++j)
73         {
74             for(int k=1;k<j;++k)
75             {
76                 int x=Q[j-k].ask(2*k);
77                 if(x!=-1)
78                     f[p1][j][k]=min(f[p1][j][k],f[p1][j-k][x]+s(k-x));
79                 Q[j].add(pt(k,f[p1][j][k]+k*k,k));
80             }
81             Q[j].add(pt(j,f[p1][j][j]+j*j,j));
82         }
83     }
84     ll ans=(ll)10000000000000;
85     int p1=n&1;
86     for(int i=1;i<=b[n];++i)
87         ans=min(ans,f[p1][b[n]][i]);
88     cout<<ans<<endl;
89 }
90 int main()
91 {
92     ios::sync_with_stdio(false);
93     cin>>n;
94     for(int i=1;i<=n;++i)
95         cin>>a[i]>>b[i];
96     solve();
97     return 0;
98 }
View Code

 

 posted on 2020-10-29 18:57  GreenDuck  阅读(149)  评论(0编辑  收藏  举报