随
给出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(" "); 111 } 112 printf(" ");*/ 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 }
单
给一个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(" "); 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 ",SUM); 118 for(int i=1;i<=n;++i) 119 printf("%d ",a[i]); 120 printf(" "); 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 }
题
你在平面直角坐标系上.
你一开始位于(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 }
总结
总的来说这套题还是不难的
我考的不好主要是刷题太少,老是没有感觉