noip模拟测试9



T1:给出n个正整数a1,a2…an和一个质数mod.一个变量x初始为1.

  进行m次操作.每次在n个数中随机选一个ai,然后x=x∗ai.问m次操作之后x的取值的期望.

  (1<=ai<mod    mod为质数    1<=mod<=1000    1<=n<=105 , 1<=m<=109

  第一眼康上去感觉是个数论加期望,完全不想做,想想后发现是个假期望,但好像是真数论,还是不想做

  于是直接跳过,最后也没什么思路,打了个n×m×mod的暴力,然后喜暴 0 

  考完后,听大佬们分享,才想到这是个矩阵优化dp

  

  那说说思路:

  首先会发现mod极小,而m极大,那么最基本的思路就是用mod复杂度增加的代价将m的复杂度降低

  猜测m复杂度为log级别,那么就想log级的算法

  想到如果设计一个状态 f [ i ][ j ] 表示操作 i 此后变成 j 的期望

  那么每次转移的系数矩阵都是相同的!

  我们就可以用矩阵快速幂来优化dp的转移,于是复杂度为:O ( mod3 log(m) )

  

  可是还是过不了,思路有问题吗?显然没有,那怎么优化呢?

  关于矩乘的优化,想到循环矩阵的优化,于是看能不能转变系数矩阵的定义,让其成为循环矩阵

  发现题目一个很妙的性质,1<=ai<mod,于是想到用原根优化(???)

  设原根为 rt ,若 i = xp[i] 那么,我们最开始的式子是 i × ak → j (mod mod)

  那么用原根就可以将式子转化为 xp[i] × xp[ak] = xp[j] (mod mod)

  因为底数都是 x ,所以我们可以将其转化为指数间的运算

  即:p[ i ] + p[ ak ] = p[ j ] (mod φ(mod))

  

  哇,加法!

  好了,它循环了。

  为什么? 加法的转移相当与一种等距离的定向的转移,所以必循环

  时间复杂度变为 O ( mod2 log(m) ) ,同时还优化了空间的复杂度

  

  so,code

  

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<vector>
 7 #include<queue>
 8 #define ll long long
 9 using namespace std;
10 const int MAXMOD=1005,MAXN=100005,D=1e9+7;
11 int n,m,mod;
12 ll val[MAXN],ans,prt,pos[MAXMOD],cnt[MAXMOD],stk[MAXMOD],tp,invn;
13 struct Matrix {
14     ll s[MAXMOD];
15     Matrix() {memset(s,0,sizeof(s));}
16 }P,R;
17 Matrix operator * (const Matrix &AA,const Matrix &BB) {
18     Matrix CC;
19     for(int i=0;i<mod;i++)
20         for(int j=0;j<mod;j++)
21             CC.s[(i+j)%(mod-1)]=(CC.s[(i+j)%(mod-1)]+AA.s[i]*BB.s[j]%D)%D;
22     return CC;
23 }
24 ll qpow(ll x,ll k,ll dd) {
25     ll ret=1;
26     while(k) {
27         if(k&1) ret=(ret*x)%dd;
28         x=(x*x)%dd,k>>=1;
29     }
30     return ret%dd;
31 }
32 void get_prt() {
33     int tmp=mod-1;
34     for(int i=2;i*i<mod;i++)
35         if(tmp%i==0) {
36             stk[++tp]=i;
37             while(tmp%i==0) tmp/=i;
38         }
39     if(tmp>1) stk[++tp]=tmp;
40     for(int i=2;i<=mod;i++) {
41         bool flag=0;
42         for(int j=1;j<=tp;j++)
43             if(qpow(i,(mod-1)/stk[j],mod)==1) {flag=1;break;}
44         if(!flag) {prt=i;break;}
45     }
46     for(int i=0;i<mod-1;i++) pos[qpow(prt,i,mod)]=i;
47 }
48 int main() {
49     scanf("%d%d%d",&n,&m,&mod);
50     for(int i=1;i<=n;i++) scanf("%lld",&val[i]),++cnt[val[i]];
51     get_prt();
52     invn=qpow(n,D-2,D);
53     for(int i=1;i<mod;i++)
54         P.s[pos[i]]=cnt[i]*invn%D;
55     R.s[0]=1;
56     while(m) {
57         if(m&1) R=R*P;
58         P=P*P,m>>=1;
59     }
60     for(int i=1;i<mod;i++) ans=(ans+R.s[pos[i]]*i)%D;
61     printf("%lld\n",ans);
62     return 0;
63 }

t1 Code




T2:给一颗树,每个节点都有两个权值a和b,a和b有如下关系:

  b[x]=a[1]dis(1,x)+a[2]dis(2,x)+....+a[n]*dis(n,x) (dis ( i , j ) 表示i 到j 的最短路径经过的边数)

  现在给你a与b数组中的一个,求另一个数组。

  ( 2<=n<=100000 )

  首先很容易就可以在 O ( n ) 的复杂度下利用换根dp用a求出b,dp方程显然,就不赘述了

  

  主要看如何用b来求a:

  不妨设dp的根为1

  我们看a求b时换根的方程 b[ v ] = b[ u ] + (siz[ 1 ] - siz [ v ])- siz [ v ]

  (siz [ i ] 表示以i为根的子树内a的和)(v是u的儿子)

  移项后得 b[ v ] - b[ u ] = siz[ 1 ] - 2×siz[ v ] 记作 g[ v ]

  于是我们可以对除1以外的所以点进行上述计算

  然后会发现其实并算不出什么东西???

  可是我们还没有用 b[ 1 ] 啊?

  于是将 b[ 1 ]的表达式写出,然后再合并

  发现,b[ 1 ] = ∑siz [ u ] (u!=1)

  那就简单了,用 ∑g[ v ] + 2 × b[ 1 ] 就可以计算出 siz[ 1 ] 的值

  然后再用 g 和 siz[ 1 ] 算出所有点的 siz

  最后dfs一遍求出a即可

  

  so,code

  

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<vector>
 7 #include<queue>
 8 #define ll long long
 9 using namespace std;
10 const int MAXN=200005;
11 int T,n,o;
12 ll val[MAXN],siz[MAXN],f[MAXN],g[MAXN],sum;
13 struct node {
14     int to,nxt;
15 }mp[MAXN*2];
16 int h[MAXN],tot;
17 void add(int x,int y) {
18     mp[++tot].nxt=h[x];
19     mp[tot].to=y;
20     h[x]=tot;
21 }
22 void dfs1(int u,int fa) {
23     siz[u]=val[u];
24     for(int i=h[u];i;i=mp[i].nxt) {
25         int v=mp[i].to;
26         if(v==fa) continue;
27         dfs1(v,u);
28         siz[u]+=siz[v];
29         f[u]+=f[v]+siz[v];
30     }
31 }
32 void dfs2(int u,int fa) {
33     for(int i=h[u];i;i=mp[i].nxt) {
34         int v=mp[i].to;
35         if(v==fa) continue;
36         g[v]=g[u]-siz[v]+(siz[1]-siz[v]);
37         dfs2(v,u);
38     }
39 }
40 void work0() {
41     dfs1(1,0);
42     g[1]=f[1];
43     dfs2(1,0);
44     for(int i=1;i<=n;i++) printf("%lld ",g[i]);
45     printf("\n");
46 }
47 void dfs3(int u,int fa) {
48     for(int i=h[u];i;i=mp[i].nxt) {
49         int v=mp[i].to;
50         if(v==fa) continue;
51         g[v]=val[v]-val[u];
52         dfs3(v,u);
53     }
54 }
55 void dfs4(int u,int fa) {
56     f[u]=siz[u];
57     for(int i=h[u];i;i=mp[i].nxt) {
58         int v=mp[i].to;
59         if(v==fa) continue;
60         dfs4(v,u);
61         f[u]-=siz[v];
62     }
63 }
64 void work1() {
65     dfs3(1,0);
66     for(int i=2;i<=n;i++) sum+=g[i];
67     siz[1]=(2*val[1]+sum)/(n-1);
68     for(int i=2;i<=n;i++) siz[i]=(siz[1]-g[i])/2;
69     dfs4(1,0);
70     for(int i=1;i<=n;i++) printf("%lld ",f[i]);
71     printf("\n");
72 }
73 int main() {
74     scanf("%d",&T);
75     while(T--) {
76         scanf("%d",&n);
77         for(int i=1,aa,bb;i<n;i++) scanf("%d%d",&aa,&bb),add(aa,bb),add(bb,aa);
78         scanf("%d",&o);
79         for(int i=1;i<=n;i++) scanf("%lld",&val[i]);
80         if(!o) work0();
81         else work1();
82         memset(h,0,sizeof(h));
83         memset(f,0,sizeof(f));
84         memset(g,0,sizeof(g));
85         memset(siz,0,sizeof(siz));
86         memset(val,0,sizeof(val));
87         tot=0;sum=0;
88     }
89     return 0;
90 }

t2 Code




T3:先写会儿插头dp,咕着。。。

原文地址:https://www.cnblogs.com/Gkeng/p/11259008.html

时间: 2024-07-31 10:36:17

noip模拟测试9的相关文章

noip模拟测试11

T1:string 第一眼秒出思路,这不就是排序那道题的加强版吗? 然而歪?解复杂度虽然是对的,但常数过大,竟被卡到70 歪?解:(实际上std写的就是这个,但据说std被卡掉了 OAO) 因为字符集很小,所以我们可以把区间排序改为区间查询和覆盖 即:先查询区间内所有字符的个数,再从左端点开始按照大小关系依次将长度为字符个数的区间修改为该字符. 期望复杂度O ( 26*mlogn ),实际复杂度O ( 26*mlogn*(巨大的常数) ) 所以需要一(feng)定(kuang)的卡常 正?解:

noip模拟测试21

T1:折纸 这道写崩我也是没话说…… 模拟就完了,记录每次的折叠点,每次将之前的都扫一遍就完了 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<cmath> 5 #include<algorithm> 6 #include<cstdlib> 7 #define ll long long 8 using namespace std; 9 con

2019.5.18 Noip模拟测试

总结: 第一题gcd水题,不开long long见祖宗 第二题求逆序对,看不出来 第三题暴力都不会 总分30+0+0=30 真的是垃圾 原文地址:https://www.cnblogs.com/LJB666/p/10885381.html

2019.5.25 Noip模拟测试2

原文地址:https://www.cnblogs.com/LJB666/p/10921986.html

noip模拟测试17 2019-08-11 题目解析

35+60+8 T1暴力,没有想到二维前缀和搞了个四维数组硬刚没敢开大,开大没有MLE多了15分; T2贪心正解,然而没开两倍边,dfs时候sd的只压了叶子?60->75->100 T3 rand数... 不行啊,差距太大了,时间分配不均,思维太傻,,,T1一点感觉没有,T2想了将近1个小时多,而且暴力打的慢,T1暴力打了将近一个小时......T3想打状压来着的,权衡一下24没有T1多... 思维  考试  心态  时间 考试状态并不是特别好,顾虑太多,感觉想不到正解又不愿意放弃,就在这种矛

模拟测试(vj)

做这份模拟测试,已经崩溃了,英文看不懂,题意理解错.到结束了只a了第一题,人生陷入了低谷,于是花了一天的时间终于把不会的弄明白了,在这里写一份总结~ T1,简单的模拟,如果打枪打中一支鸟,将这个位置设为0,并向两边扩散,注意这个位置一定要有鸟. 代码~ #include<bits/stdc++.h> using namespace std; int a[30000]; int n,m; int main() { cin>>n; for(int i=1;i<=n;i++) ci

NOIP模拟17.8.17

NOIP模拟17.8.17 A 小 G 的字符串文件名 输入文件 输出文件 时间限制 空间限制str.pas/c/cpp str.in str.out 1s 128MB[题目描述]有一天,小 L 给小 G 出了这样一道题:生成一个长度为 n 的.全由小写英文字母构成的字符串,只能使用 k 种字母.要求满足:• 字符串中相邻的两个字母不能相同.• 必须出现恰好 k 种不同的字母.这样的合法字符串可能有很多,小 L 让小 G 输出字典序最小的那个.小 G 太笨啦,不会做这道题,希望你帮帮他.[输入格

Android单元测试与模拟测试详解

测试与基本规范 为什么需要测试? 为了稳定性,能够明确的了解是否正确的完成开发. 更加易于维护,能够在修改代码后保证功能不被破坏. 集成一些工具,规范开发规范,使得代码更加稳定( 如通过 phabricator differential 发diff时提交需要执行的单元测试,在开发流程上就可以保证远端代码的稳定性). 2. 测什么? 一般单元测试: 列出想要测试覆盖的异常情况,进行验证. 性能测试. 模拟测试: 根据需求,测试用户真正在使用过程中,界面的反馈与显示以及一些依赖系统架构的组件的应用测

[BZOJ入门OJ2092][Noip模拟题]舞会

2092: [Noip模拟题]舞会 Time Limit: 20 Sec  Memory Limit: 256 MB Submit: 9  Solved: 5 [Submit][Status][Web Board] Description 学校举行舞会啦,一共有N个人参加,所有人站成一排,从左开始编号,最左边的人编号为1 ,最右边的为N.每个人跳舞的熟练度我们用一个整数表示,第i个人的熟练度为Ai,每次熟 练度最接近的一对相邻男女会出列跳舞,如果有多对那么最左边的那一对会先出列,请你给 出出列跳