2017.8.29 达哥的随单题...

给出n个正整数a1,a2…an和一个质数mod.一个变量x初始为1.进行m次操作.每次在n个数中随机选一个ai,然后x=x*ai%mod.问m次操作之后x的取值的期望.

答案一定可以表示成a/b的精确分数形式.a和b可能很大,所以只需要输出a*(b^(10^9+5))模10^9+7的结果.

n<=10^5  m<=10^9  mod<=10^3  1<=ai<mod

solution

考试时明白了原根是什么,只可惜我就是脑子抽风,不会dp...

80分:

定义 f[i][j] 表示乘到第i次,x=j的概率 (当然概率是%1e9意义下的概率)

由于m很大,而状态转移可以写成矩阵乘的形式,所以可以用矩阵乘法优化到O(mod^3*logm)

100分:

O(n^2)求出mod的原根(原根rt:rt的1次方,2次方,...,mod-1次方可以取到1~mod-1的所有数)

所有的ai都可以用rt^k表示

所以这时 f[i][j] 表示乘到第i次,rt^j的概率,然后矩阵乘把状态矩阵输出,发现是循环矩阵

之后就优化到了O(n^2*logm)

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <iostream>
  4 #define mem(a,b) memset(a,b,sizeof(a))
  5 #define ll long long
  6 using namespace std;
  7 const int MOD=1000000007;
  8 const int N=100066;
  9  
 10 int n,m,mod;
 11 int a2[N],num[1066],dui[1066];
 12 int rt,mod_1;
 13 ll a[1066][1066],f[1066],temp[1066][1066],gai;
 14 bool flag[1066];
 15  
 16 ll mi(ll a,int ci,int c)
 17 {
 18   ll ans=1;
 19   while(ci)
 20   {
 21     if(ci&1)
 22       ans=ans*a%c;
 23     a=a*a%c;
 24     ci>>=1;
 25   }
 26   return ans;
 27 }
 28  
 29 int get_rt()
 30 {
 31   int judge,temp;
 32   for(int i=1;i<mod;++i)
 33   {
 34     judge=0;temp=1;
 35     mem(flag,0);
 36     for(int j=1;j<mod;++j)
 37     {
 38       temp=temp*i%mod;
 39       if(flag[temp])
 40       {
 41         judge=1;
 42         break;
 43       }
 44       flag[temp]=1;
 45     }
 46     if(!judge)
 47       return i;
 48   }
 49 }
 50  
 51 void get_a2_dui()
 52 {
 53   int temp=1;
 54   for(int i=1;i<mod;++i)
 55   {
 56     temp=temp*rt%mod;
 57     dui[temp]=i;
 58   }
 59 }
 60  
 61 void get_num()
 62 {
 63   for(int i=1;i<=n;++i)
 64     ++num[dui[a2[i]]];
 65 }
 66  
 67 void build_a()
 68 {
 69   for(int i=0;i<mod;++i)
 70     for(int j=1;j<mod;++j)
 71       a[i][(i+j)%mod_1]=(a[i][(i+j)%mod_1]+gai*num[j]%MOD)%MOD;
 72 }
 73  
 74 void cheng()
 75 {
 76   f[0]=1;
 77   while(m)//横着是<mod-1,纵<mod
 78   {
 79     if(m&1)
 80     {
 81       for(int i=0;i<=mod;++i)
 82       {
 83         temp[0][i]=0;
 84         for(int k=0;k<=mod;++k)
 85           temp[0][i]=(temp[0][i]+f[k]*a[k][i]%MOD)%MOD;
 86       }
 87       for(int i=0;i<=mod;++i)
 88         f[i]=temp[0][i];
 89     }
 90     for(int i=0;i<=mod;++i)
 91     {
 92       temp[0][i]=0;
 93         for(int k=0;k<=mod;++k)
 94           temp[0][i]=(temp[0][i]+a[0][k]*a[k][i]%MOD)%MOD;
 95     }
 96      
 97     for(int i=0;i<=mod;++i)
 98       a[0][i]=temp[0][i];
 99      
100     for(int i=1;i<=mod;++i)
101     {
102       a[i][0]=a[i-1][mod_1-1];
103       for(int j=1;j<mod_1;++j)
104         a[i][j]=a[i-1][j-1];
105     }
106     /*for(int i=0;i<mod;++i)
107     {
108       for(int j=0;j<mod_1;++j)
109         printf("%lld ",a[i][j]);
110       printf("\n");
111     }
112     printf("\n");*/
113     m>>=1;
114   }
115 }
116  
117 int main(){
118      
119   //freopen("rand4.in","r",stdin);
120   //freopen("2.txt","w",stdout);
121    
122   scanf("%d%d%d",&n,&m,&mod);
123   mod_1=mod-1;
124   for(int i=1;i<=n;++i)
125     scanf("%d",&a2[i]);
126   gai=mi(n,MOD-2,MOD);
127   rt=get_rt();
128   get_a2_dui();
129   get_num();
130   build_a();
131   cheng();
132   ll ans=0,temp=1;
133   ans=(ans+temp*f[0]%MOD)%MOD;
134   for(int i=1;i<=mod;++i)
135   {
136     temp=temp*rt%mod;
137     ans=(ans+temp*f[i]%MOD)%MOD;
138   }
139   cout<<ans;
140 }
code

给一个n个点的树,有n-1条边,每一个点有一个权值,定义a[i]为i这个点的权值,定义dis(i,j)为i到j的树上距离,dis(i,i)=0

定义b[i]=∑a[j]*dis(i,j)

有两种情况:

1.给定a[i],求出b[i]

2.给定b[i],求出a[i]

solution

对于树上的一对fa和son,我们发现b[fa]和b[son]的差别只是由于他俩之间的边做出贡献

定义 sum为整棵树的取值之和 pre[i]=sum-val[i](以i为根的子树权值和) suf[i]=val[i]

而 pre[i]和suf[i] 都是可以通过一遍O(n)的dfs求出

那么得到n-1个关系 b[fa]-b[son]=-pre[fa]+suf[son]

第一种情况:

由b[fa]可以推到b[son] 即

b[son]=b[fa]+pre[fa]-suf[fa]

所以先dfs一遍求出b[root]

再递推即可

第二种情况:

b[fa]-b[son]=-pre[fa]+suf[son]+ sum=pre[fa]+suf[son]

                  ↓

b[fa]-b[son]=2*suf[son]-sum

把n-1个关系相加得

temp=2*(∑suf[k](1<=k<=n,k!=root))-(n-1)*sum的值

得到的n-1个关系 只是a[i]之间的关系,与它们的具体取值无关

而 b[root]=∑suf[k](1<=k<=n,k!=root)

(temp+b[root]*2)/(n-1)=sum

求出来sum再回代即可求出suf[i],最后差分求出a[i]

  1 #include <cstdio>
  2 #include <iostream>
  3 #include <cstring>
  4 #define mem(a,b) memset(a,b,sizeof(a))
  5 #define ll long long
  6 using namespace std;
  7 const int N=200066;
  8 int read()
  9 {
 10   int ans=0,flag=1;char q=getchar();
 11   while(q<'0'||q>'9'){if(q=='-')flag=-1;q=getchar();}
 12   while(q>='0'&&q<='9'){ans=ans*10+q-'0';q=getchar();}
 13   return ans*flag;
 14 }
 15 struct son
 16 {
 17     int v,next;
 18 };
 19 son a1[N*4];
 20 int first[N*4],e;
 21 void addbian(int u,int v)
 22 {
 23   a1[e].v=v;
 24   a1[e].next=first[u];
 25   first[u]=e++;
 26 }
 27  
 28 int T;
 29 int kk,n;
 30 int u,o;
 31 int a[N],b[N];
 32  
 33 int fa[N];
 34  
 35 int val[N],dep[N];
 36 void dfs0(int x)
 37 {
 38   val[x]=a[x];
 39   for(int i=first[x];i!=-1;i=a1[i].next)
 40   {
 41     int temp=a1[i].v;
 42     if(temp==fa[x])continue;
 43     dep[temp]=dep[x]+1;
 44     fa[temp]=x;
 45     dfs0(temp);
 46     val[x]+=val[temp];
 47   }
 48 }
 49  
 50 void dfs00(int x)
 51 {
 52   for(int i=first[x];i!=-1;i=a1[i].next)
 53   {
 54     int temp=a1[i].v;
 55     if(fa[x]==temp)continue;
 56     b[temp]=b[x]+val[1]-val[temp]-val[temp];
 57     dfs00(temp);
 58   }
 59 }
 60  
 61 void work0()
 62 {
 63   for(int i=1;i<=n;++i)
 64     a[i]=read();
 65   mem(b,0);mem(fa,0);mem(val,0);
 66   mem(dep,0);
 67    
 68   dfs0(1);
 69   for(int i=2;i<=n;++i)
 70     b[1]+=dep[i]*a[i];
 71   dfs00(1);
 72   for(int i=1;i<=n;++i)
 73     printf("%d ",b[i]);
 74   printf("\n");
 75 }
 76  
 77 void dfs1(int x)
 78 {
 79   for(int i=first[x];i!=-1;i=a1[i].next)
 80   {
 81     int temp=a1[i].v;
 82     if(temp==fa[x])continue;
 83     fa[temp]=x;
 84     a[temp]=b[x]-b[temp];
 85     dfs1(temp);
 86   }
 87 }
 88  
 89 void dfs11(int x)
 90 {
 91   for(int i=first[x];i!=-1;i=a1[i].next)
 92   {
 93     int temp=a1[i].v;
 94     if(temp==fa[x])continue;
 95     a[x]-=a[temp];
 96     dfs11(temp);
 97   }
 98 }
 99  
100 void work1()
101 {
102   for(int i=1;i<=n;++i)
103     b[i]=read();
104   mem(a,0);mem(fa,0);
105    
106   dfs1(1);
107   ll SUM=0;
108   for(int i=2;i<=n;++i)
109     SUM+=a[i];
110   SUM-=(2*b[1]);
111   SUM=-SUM;
112   SUM/=(n-1);
113   for(int i=2;i<=n;++i)
114     a[i]=((a[i]+SUM)>>1);
115   a[1]=SUM;
116   dfs11(1);
117   //printf("SUM=%d\n",SUM);
118   for(int i=1;i<=n;++i)
119     printf("%d ",a[i]);
120   printf("\n");
121 }
122  
123 int main(){
124    
125   //freopen("single9.in","r",stdin);
126   //freopen("2.txt","w",stdout);
127    
128   T=read();
129   while(T--)
130   {
131     mem(a1,0);mem(first,-1);e=0;
132     n=read();
133     for(int i=1;i<n;++i)
134     {
135       u=read();o=read();
136       addbian(u,o);
137       addbian(o,u);
138     }
139     kk=read();
140     if(kk==1)work1();
141     else work0();
142   }
143 }
code

你在平面直角坐标系上.

你一开始位于(0,0).

每次可以在上/下/左/右四个方向中选一个走一步.

即:从(x,y)走到(x,y+1),(x,y-1),(x-1,y),(x+1,y)四个位置中的其中一个.

允许你走的步数已经确定为n.现在你想走n步之后回到(0,0).但这太简单了.你希望知道有多少种不同的方案能够使你在n步之后回到(0,0).当且仅当两种方案至少有一步走的方向不同,这两种方案被认为是不同的.

答案可能很大所以只需要输出答案对10^9+7取模后的结果.(10^9+7=1000000007,1和7之间有8个0)

这还是太简单了,所以你给能够到达的格点加上了一些限制.一共有三种限制,加上没有限制的情况,一共有四种情况,用0,1,2,3标号:

0.没有任何限制,可以到达坐标系上所有的点,即能到达的点集为{(x,y)|x,y为整数}

1.只允许到达x轴非负半轴上的点.即能到达的点集为{(x,y)|x为非负数,y=0}

2.只允许到达坐标轴上的点.即能到达的点集为{(x,y)|x=0或y=0}

3.只允许到达x轴非负半轴上的点,y轴非负半轴上的点以及第1象限的点.即能到达的点集为{(x,y)|x>=0,y>=0}

typ=2,n<=1000  typ=3,n<=100000   typ=1,n<=100000  typ=0,n<=100000

solution

注意一下走的步数必须是偶数


tpy=0

ans=∑( C(n,i)*C(i,i/2)*C(n-i,(n-i)/2) )

解释:

C(n,i)是从n步里选出i步水平走

C(i,i/2)是从i步里选出i/2步向右走

C(n-i,(n-i)/2)是从n-i步里选出(n-i)/2步向上走


tpy=1

ans=Catalan[n/2]

解释:

向左、右走 就相当于 左、右括号


tpy=2

定义 f[i] 为走i步回到原点的方案数

f[i]=∑( f[k]*Catalan[(i-k)/2-1]*4 )

ans=f[n]

解释:

Catalan[(i-k)/2-1]意为走i-k步第一次回到原点的方案数 = (0,1)向上走再回来的方案数


tpy=3

ans=∑( C(n,i)*Catalan[i]*Catalan[ (n-i)/2 ] )

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #define ll long long
 5 #define C(n,m) (jie[(n)]*jieni[(m)]%mod*jieni[(n)-(m)]%mod)
 6 #define can(n) (C(((n)<<1),(n))*ni[(n)+1]%mod)
 7 using namespace std;
 8 const int mod=1000000007;
 9 const int N=100066;
10  
11 int n,kk;
12  
13 ll jie[N],jieni[N],ni[N];
14 void chu()
15 {
16   ni[1]=1;
17   for(int i=2;i<N;++i)
18     ni[i]=(ll)(mod-mod/i)*ni[mod%i]%mod;
19    
20   jie[0]=jieni[0]=jie[1]=jieni[1]=1;
21   for(int i=2;i<N;++i)
22   {
23     jie[i]=jie[i-1]*(ll)i%mod;
24     jieni[i]=jieni[i-1]*ni[i]%mod;
25   }
26 }
27  
28 void work0()
29 {
30   ll ans=0,temp;
31   for(int i=0;i<=n;i+=2)
32   {
33     temp=1;
34     temp=temp*C(n,i)%mod*C(i,(i>>1))%mod*C((n-i),((n-i)>>1))%mod;
35     ans=(ans+temp)%mod;
36   }
37   cout<<ans;
38 }
39  
40 void work1()
41 {
42   cout<<can((n>>1));
43 }
44  
45 ll f[N];
46 void work2()
47 {
48   f[0]=1;
49   for(int i=2;i<=n;i+=2)
50     for(int j=2;j<=i;j+=2)
51       f[i]=(f[i]+f[i-j]*can((j>>1)-1)%mod*4%mod)%mod;
52   cout<<f[n];
53 }
54  
55 void work3()
56 {
57   ll ans=0,temp;
58   for(int i=0;i<=n;i+=2)
59   {
60     temp=1;
61     temp=temp*C(n,i)%mod*can((i>>1))%mod*can(((n-i)>>1))%mod;
62     ans=(ans+temp)%mod;
63   }
64   cout<<ans;
65 }
66  
67 int main(){
68    
69   //freopen("problem.in","r",stdin);
70   //freopen("problem.out","w",stdout);
71    
72   chu();
73    
74   scanf("%d%d",&n,&kk);
75    
76   if(kk==0)work0();
77   else if(kk==1)work1();
78   else if(kk==2)work2();
79   else work3();
80    
81   return 0;   
82 }
code

总结

总的来说这套题还是不难的

我考的不好主要是刷题太少,老是没有感觉

转载于:https://www.cnblogs.com/A-LEAF/p/7449314.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值