T1 数数

解题思路

大概是一个签到题的感觉。。。(但是 pyt 并没有签上)

第一题当然可以找规律,但是咱们还是老老实实搞正解吧。。。

先从小到大拍个序,这样可以保证 \(a_l<a_r\) 直接去掉绝对值。

然后就可以推出如下柿子:

\[\displaystyle\sum_{l=1}^{k}-a_l\times(k-l)+\sum_{r=2}^{k}a_r\times a_r(r-1)
\]
\[\displaystyle\sum_{i=1}^{k}a_i\times (2\times i-k-1)
\]

然后就会发现 \(a_i\) 的贡献的正负与下标有关系那么对于 \(i<\dfrac{k+1}{2}\) 尽量选择小的数字。

同样的,对于 \(i>\dfrac{k+1}{2}\) 尽量选择比较大的数字。

对于不同的 \(k\) 每一个 \(a_i\) 的系数都是会发生变化的,简单处理一下就好了。

code

  1. #include<bits/stdc++.h>
  2. #define int long long
  3. #define ull unsigned long long
  4. #define f() cout<<"Pass"<<endl
  5. using namespace std;
  6. inline int read()
  7. {
  8. int x=0,f=1;char ch=getchar();
  9. while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
  10. while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
  11. return x*f;
  12. }
  13. const int N=3e5+10;
  14. int n,l,r,sum1,sum2,pre[N],s[N];
  15. signed main()
  16. {
  17. n=read();
  18. for(int i=1;i<=n;i++)
  19. s[i]=read();
  20. l=0; r=n+1;
  21. sort(s+1,s+n+1);
  22. for(int i=1;i<=n;i++)
  23. pre[i]=pre[i-1]+s[i];
  24. for(int k=1;k<=n;k++)
  25. {
  26. int pos=(k+1)/2;
  27. sum1-=pre[l]; sum2+=pre[n]-pre[r-1];
  28. while(l<pos) l++,sum1+=s[l]*(2*l-k-1);
  29. while(n-r+1<pos) r--,sum2+=s[r]*(2*(-n+r)+k-1);
  30. printf("%lld\n",sum2+sum1);
  31. }
  32. return 0;
  33. }

T2 数树

解题思路

树上背包 DP 。

\(f_{i,j,0/1/2/3}\) 表示 节点 \(i\) 目前至少有 \(j\) 条不合法的边,并且当前节点状态是:没有出入边,有一条出边,有一条入边,有一条出边一条入边。

然后进行树形 DP 子树合并计算出叶子节点对于自身的贡献,当然也要把没有贡献的继承过来。

最后算出来的结果运用一个类似于二项式反演的东西就可以得到恰好没有不合法边的状态。

为什么说是类似呢,它的系数是有一些差别的 \((n-i)!\) ,毕竟对于有 \(i\) 条不合法的边而言,它所链接的两个点其实可以看作是一个点,然后就是一个数值的排问题了。

code

  1. #include<bits/stdc++.h>
  2. #define int long long
  3. #define ull unsigned long long
  4. #define f() cout<<"Pass"<<endl
  5. using namespace std;
  6. inline int read()
  7. {
  8. int x=0,f=1;char ch=getchar();
  9. while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
  10. while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
  11. return x*f;
  12. }
  13. const int N=5e3+10,mod=998244353;
  14. int n,ans,fac[N],siz[N],f[N][N][4],g[N][4];
  15. int tot,head[N],nxt[N<<1],edge[N<<1],ver[N<<1];
  16. void add_edge(int x,int y,int val)
  17. {
  18. ver[++tot]=y;
  19. nxt[tot]=head[x];
  20. edge[tot]=val;
  21. head[x]=tot;
  22. }
  23. void dfs(int x)
  24. {
  25. siz[x]=1; f[x][0][0]=1;
  26. for(int i=head[x];i;i=nxt[i])
  27. {
  28. int to=ver[i];
  29. if(siz[to]) continue;
  30. dfs(to);
  31. memset(g,0,sizeof(g));
  32. for(int j=0;j<siz[x];j++)
  33. for(int k=0;k<siz[to];k++)
  34. {
  35. int cnt=(f[to][k][0]+f[to][k][1]+f[to][k][2]+f[to][k][3])%mod;
  36. switch(edge[i])
  37. {
  38. case 1:
  39. (g[j+k+1][1]+=f[x][j][0]*(f[to][k][0]+f[to][k][1]))%=mod;
  40. (g[j+k+1][3]+=f[x][j][2]*(f[to][k][0]+f[to][k][1]))%=mod;
  41. break;
  42. case 0:
  43. (g[j+k+1][2]+=f[x][j][0]*(f[to][k][0]+f[to][k][2]))%=mod;
  44. (g[j+k+1][3]+=f[x][j][1]*(f[to][k][0]+f[to][k][2]))%=mod;
  45. break;
  46. }
  47. (g[j+k][0]+=cnt*f[x][j][0])%=mod;
  48. (g[j+k][1]+=cnt*f[x][j][1])%=mod;
  49. (g[j+k][2]+=cnt*f[x][j][2])%=mod;
  50. (g[j+k][3]+=cnt*f[x][j][3])%=mod;
  51. }
  52. siz[x]+=siz[to];
  53. for(int j=0;j<siz[x];j++)
  54. {
  55. f[x][j][0]=g[j][0];
  56. f[x][j][1]=g[j][1];
  57. f[x][j][2]=g[j][2];
  58. f[x][j][3]=g[j][3];
  59. }
  60. }
  61. }
  62. signed main()
  63. {
  64. n=read();
  65. for(int i=1,x,y;i<n;i++)
  66. {
  67. x=read(); y=read();
  68. add_edge(x,y,1); add_edge(y,x,0);
  69. }
  70. dfs(1); fac[0]=1;
  71. for(int i=1;i<=n;i++)
  72. fac[i]=fac[i-1]*i%mod;
  73. for(int i=0,temp;i<n;i++)
  74. {
  75. temp=0;
  76. (temp+=f[1][i][0])%=mod;
  77. (temp+=f[1][i][1])%=mod;
  78. (temp+=f[1][i][2])%=mod;
  79. (temp+=f[1][i][3])%=mod;
  80. if(i&1) (ans+=mod-fac[n-i]*temp%mod)%=mod;
  81. else (ans+=fac[n-i]*temp%mod)%=mod;
  82. }
  83. printf("%lld",ans);
  84. return 0;
  85. }

T3 鼠树

解题思路

其实题写的挺艰辛的。。

考场上是想到了一个两颗线段树维护(一棵维护归属点,一棵维护权值)的办法,但是貌似单次修改的复杂度是 \(nlog^2n\) 的,还不如暴力。。

最主要的是它的 3 操作还是有问题的。。(\(code\)

正解的做法就比较神仙了。

开一个 set 维护单个点的归属点,具体实现主要结合树链剖分,维护每一条链最顶端的点 set 并且记录该 set 里的深度最小值,与目前的点的深度相比较。

时间复杂度大概是 \(log\;n\) ,这里的 set 是按照深度由小到大排的序。

还有两棵线段树,一个维护黑色节点在它的所有管辖点应该下放的权值还有范围以及权值和,另一个维护因为节点操作的缘故,而出现的比较杂碎的权值。

第二个的话,其实就是区间修改,区间查询的线段树,也可以用树状数组来实现。

为了方便下文将会把第一颗线段树称为 T1 ,第二颗称为 T2 。

实现的话分操作讲一下吧。。

  • 1 操作除了需要查询归属点应该下放的权值外也要计算一下 T2 里面琐碎的权值。

  • 2 操作直接在 T1 里单点修改权值就好了。

  • 3 操作查询 T1 子树范围内的管辖点应下放的权值与范围的乘积,也要算上计算 T2 子树范围内的权值和,还有就是对于子树根节点向下一部分的点,他们的管辖点可能是在子树之外的,这里也需要算进去。

  • 4 操作直接给 T1 区间修改就好了。

  • 5 操作就开始有一点恶心人了,先是查找更改之前当前节点(计为 \(x\) )的归属点(计为 \(att\)),将现在应该加到 \(x\) 上的范围减去,然后将范围加入到 \(x\) 上,对于之前的下放权值继承到 \(x\) 上。

  • 6操作与 5 操作有一些类似,同样是范围的加减继承,同样的把相对于 \(att\) 多的权值区间修改到 T2 上,这也就是维护两颗线段树的意义所在。

code

  1. #include<bits/stdc++.h>
  2. #define ui unsigned int
  3. #define ull unsigned long long
  4. #define f() cout<<"Pass"<<endl
  5. #define ls x<<1
  6. #define rs x<<1|1
  7. using namespace std;
  8. inline int read()
  9. {
  10. int x=0,f=1;char ch=getchar();
  11. while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
  12. while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
  13. return x*f;
  14. }
  15. const int N=1e6+10,INF=1e9;
  16. int n,m,minn[N];
  17. int tot,head[N],nxt[N<<1],ver[N<<1];
  18. int tim,dfn[N],siz[N],son[N],dep[N],fa[N],topp[N];
  19. struct Sort{bool operator ()(int x,int y){return dep[x]<dep[y];}};
  20. set<int,Sort> s[N];
  21. struct Segment_Tree1
  22. {
  23. struct Node
  24. {
  25. ui val,ran,dat;
  26. }tre[N<<2];
  27. void push_down(int x)
  28. {
  29. if(!tre[x].val) return ;
  30. if(tre[ls].ran)
  31. {
  32. tre[ls].val+=tre[x].val;
  33. tre[ls].dat+=tre[x].val*tre[ls].ran;
  34. }
  35. if(tre[rs].ran)
  36. {
  37. tre[rs].val+=tre[x].val;
  38. tre[rs].dat+=tre[x].val*1tre[rs].ran;
  39. }
  40. tre[x].val=0;
  41. }
  42. void push_up(int x)
  43. {
  44. tre[x].ran=tre[ls].ran+tre[rs].ran;
  45. tre[x].dat=tre[ls].dat+tre[rs].dat;
  46. }
  47. ui query_val(int x,int l,int r,int pos)
  48. {
  49. if(l==r) return tre[x].val;
  50. push_down(x);
  51. int mid=(l+r)>>1; ui sum=0;
  52. if(pos<=mid) sum=query_val(ls,l,mid,pos);
  53. else sum=query_val(rs,mid+1,r,pos);
  54. push_up(x);
  55. return sum;
  56. }
  57. ui query_dat(int x,int l,int r,int L,int R)
  58. {
  59. if(L<=l&&r<=R) return tre[x].dat;
  60. push_down(x);
  61. int mid=(l+r)>>1; ui sum=0;
  62. if(L<=mid) sum+=query_dat(ls,l,mid,L,R);
  63. if(R>mid) sum+=query_dat(rs,mid+1,r,L,R);
  64. push_up(x);
  65. return sum;
  66. }
  67. int query_ran(int x,int l,int r,int L,int R)
  68. {
  69. if(L<=l&&r<=R) return tre[x].ran;
  70. push_down(x);
  71. int mid=(l+r)>>1,sum=0;
  72. if(L<=mid) sum+=query_ran(ls,l,mid,L,R);
  73. if(R>mid) sum+=query_ran(rs,mid+1,r,L,R);
  74. push_up(x);
  75. return sum;
  76. }
  77. void insert_val(int x,int l,int r,int L,int R,ui num)
  78. {
  79. if(L<=l&&r<=R)
  80. {
  81. if(!tre[x].ran) return;
  82. tre[x].val+=num;
  83. tre[x].dat+=num*tre[x].ran;
  84. return ;
  85. }
  86. push_down(x);
  87. int mid=(l+r)>>1;
  88. if(L<=mid) insert_val(ls,l,mid,L,R,num);
  89. if(R>mid) insert_val(rs,mid+1,r,L,R,num);
  90. push_up(x);
  91. }
  92. void insert_ran(int x,int l,int r,int pos,ui num)
  93. {
  94. if(l==r)
  95. {
  96. tre[x].ran+=num;
  97. if(!tre[x].ran) tre[x].val=0;
  98. tre[x].dat=tre[x].val*tre[x].ran;
  99. return ;
  100. }
  101. push_down(x);
  102. int mid=(l+r)>>1;
  103. if(pos<=mid) insert_ran(ls,l,mid,pos,num);
  104. else insert_ran(rs,mid+1,r,pos,num);
  105. push_up(x);
  106. }
  107. }t1;
  108. struct Segment_Tree2
  109. {
  110. struct Node
  111. {
  112. ui dat,laz;
  113. }tre[N<<1];
  114. void push_down(int x,int l,int r)
  115. {
  116. if(!tre[x].laz) return ;
  117. int mid=(l+r)>>1;
  118. tre[ls].laz+=tre[x].laz; tre[rs].laz+=tre[x].laz;
  119. tre[ls].dat+=(mid-l+1)*tre[x].laz;
  120. tre[rs].dat+=(r-mid)*tre[x].laz;
  121. tre[x].laz=0;
  122. }
  123. void push_up(int x)
  124. {
  125. tre[x].dat=tre[ls].dat+tre[rs].dat;
  126. }
  127. void insert(int x,int l,int r,int L,int R,ui num)
  128. {
  129. if(L<=l&&r<=R)
  130. {
  131. tre[x].dat+=num*(r-l+1);
  132. tre[x].laz+=num;
  133. return ;
  134. }
  135. push_down(x,l,r);
  136. int mid=(l+r)>>1;
  137. if(L<=mid) insert(ls,l,mid,L,R,num);
  138. if(R>mid) insert(rs,mid+1,r,L,R,num);
  139. push_up(x);
  140. }
  141. ui query(int x,int l,int r,int L,int R)
  142. {
  143. if(L<=l&&r<=R) return tre[x].dat;
  144. push_down(x,l,r);
  145. int mid=(l+r)>>1; ui sum=0;
  146. if(L<=mid) sum+=query(ls,l,mid,L,R);
  147. if(R>mid) sum+=query(rs,mid+1,r,L,R);
  148. push_up(x);
  149. return sum;
  150. }
  151. }t2;
  152. void add_edge(int x,int y)
  153. {
  154. ver[++tot]=y;
  155. nxt[tot]=head[x];
  156. head[x]=tot;
  157. }
  158. void dfs1(int x)
  159. {
  160. siz[x]=1;
  161. dfn[x]=++tim;
  162. for(int i=head[x];i;i=nxt[i])
  163. {
  164. int to=ver[i];
  165. fa[to]=x; dep[to]=dep[x]+1;
  166. dfs1(to);
  167. siz[x]+=siz[to];
  168. if(siz[to]>siz[son[x]])
  169. son[x]=to;
  170. }
  171. }
  172. void dfs2(int x,int tp)
  173. {
  174. topp[x]=tp;
  175. if(son[x]) dfs2(son[x],tp);
  176. for(int i=head[x];i;i=nxt[i])
  177. if(ver[i]!=son[x])
  178. dfs2(ver[i],ver[i]);
  179. }
  180. int find_att(int x)
  181. {
  182. while(x)
  183. {
  184. if(minn[topp[x]]<=dep[x])
  185. {
  186. auto pos=s[topp[x]].upper_bound(x); pos--;
  187. return (*pos);
  188. }
  189. x=fa[topp[x]];
  190. }
  191. }
  192. void update_min(int x)
  193. {
  194. minn[x]=(s[x].size()?dep[*(s[x].begin())]:INF);
  195. }
  196. signed main()
  197. {
  198. n=read(); m=read();
  199. for(int i=2,x;i<=n;i++)
  200. x=read(),add_edge(x,i);
  201. dfs1(1); dfs2(1,1);
  202. memset(minn,0x7f,sizeof(minn));
  203. s[1].insert(1); update_min(1);
  204. t1.insert_ran(1,1,n,dfn[1],siz[1]);
  205. while(m--)
  206. {
  207. int opt,x,val,att;
  208. opt=read(); x=read();
  209. if(opt==2||opt==4) val=read();
  210. if(opt==1||opt==3||opt==5) att=find_att(x);
  211. if(opt==6) att=find_att(fa[x]);
  212. if(opt==1) printf("%u\n",t1.query_val(1,1,n,dfn[att])+t2.query(1,1,n,dfn[x],dfn[x]));
  213. if(opt==2) t1.insert_val(1,1,n,dfn[x],dfn[x],val);
  214. if(opt==3)
  215. {
  216. int att=find_att(x);
  217. ui ran=siz[x]-t1.query_ran(1,1,n,dfn[x],dfn[x]+siz[x]-1);
  218. ui sum=t2.query(1,1,n,dfn[x],dfn[x]+siz[x]-1);
  219. sum+=t1.query_dat(1,1,n,dfn[x],dfn[x]+siz[x]-1);
  220. sum+=t1.query_val(1,1,n,dfn[att])*ran;
  221. printf("%u\n",sum);
  222. }
  223. if(opt==4) t1.insert_val(1,1,n,dfn[x],dfn[x]+siz[x]-1,val);
  224. if(opt==5)
  225. {
  226. int att=find_att(x);
  227. ui ran=siz[x]-t1.query_ran(1,1,n,dfn[x],dfn[x]+siz[x]-1);
  228. ui val=t1.query_val(1,1,n,dfn[att]);
  229. t1.insert_ran(1,1,n,dfn[att],-ran);
  230. t1.insert_ran(1,1,n,dfn[x],ran);
  231. t1.insert_val(1,1,n,dfn[x],dfn[x],val);
  232. s[topp[x]].insert(x); update_min(topp[x]);
  233. }
  234. if(opt==6)
  235. {
  236. int att=find_att(fa[x]);
  237. ui ran=t1.query_ran(1,1,n,dfn[x],dfn[x]);
  238. ui val=t1.query_val(1,1,n,dfn[x]);
  239. t1.insert_ran(1,1,n,dfn[att],ran);
  240. t1.insert_ran(1,1,n,dfn[x],-ran);
  241. val-=t1.query_val(1,1,n,dfn[att]);
  242. t2.insert(1,1,n,dfn[x],dfn[x]+siz[x]-1,val);
  243. t1.insert_val(1,1,n,dfn[x],dfn[x]+siz[x]-1,-val);
  244. s[topp[x]].erase(x); update_min(topp[x]);
  245. }
  246. }
  247. return 0;
  248. }

8.23考试总结(NOIP模拟46)[数数·数树·鼠树·ckw的树]的更多相关文章

  1. 5.23考试总结(NOIP模拟2)

    5.23考试总结(NOIP模拟2) 洛谷题单 看第一题第一眼,不好打呀;看第一题样例又一眼,诶,我直接一手小阶乘走人 然后就急忙去干T2T3了 后来考完一看,只有\(T1\)骗到了\(15pts\)[ ...

  2. [考试总结]noip模拟46

    脑袋确实是不好使了需要回家暴颓治疗 数数数树鼠树 真好玩. 数数 大水题一个,妥妥的签到题目,然后... 我没签上 气展了!!! 其实我还是想麻烦了. 就是我们实际上就是排序之后每一次找头上和尾巴上的 ...

  3. 6.17考试总结(NOIP模拟8)[星际旅行·砍树·超级树·求和]

    6.17考试总结(NOIP模拟8) 背景 考得不咋样,有一个非常遗憾的地方:最后一题少取膜了,\(100pts->40pts\),改了这么多年的错还是头一回看见以下的情景... T1星际旅行 前 ...

  4. 5.22考试总结(NOIP模拟1)

    5.22考试总结(NOIP模拟1) 改题记录 T1 序列 题解 暴力思路很好想,分数也很好想\(QAQ\) (反正我只拿了5pts) 正解的话: 先用欧拉筛把1-n的素数筛出来 void get_Pr ...

  5. 2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色

    2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 数据结构学傻的做法: 对每种颜色开动态开点线段树直接维 ...

  6. [考试总结]noip模拟23

    因为考试过多,所以学校的博客就暂时咕掉了,放到家里来写 不过话说,vscode的markdown编辑器还是真的很好用 先把 \(noip\) 模拟 \(23\) 的总结写了吧.. 俗话说:" ...

  7. Noip模拟46 2021.8.23

    给了签到题,但除了签到题其他的什么也不会.... T1 数数 人均$AC$,没什么好说的,就是排个序,然后双指针交换着往中间移 1 #include<bits/stdc++.h> 2 #d ...

  8. 2021.9.17考试总结[NOIP模拟55]

    有的考试表面上自称NOIP模拟,背地里却是绍兴一中NOI模拟 吓得我直接文件打错 T1 Skip 设状态$f_i$为最后一次选$i$在$i$时的最优解.有$f_i=max_{j<i}[f_j+a ...

  9. 2021.7.21考试总结[NOIP模拟22]

    终于碾压小熠了乐死了 T1 d 小贪心一波直接出正解,没啥好说的(bushi 好像可以主席树暴力找,但我怎么可能会呢?好像可以堆优化简单找,但我怎么可能想得到呢? 那怎么办?昨天两道单调指针加桶,我直 ...

随机推荐

  1. ctf常见编码形式(罗师傅)

    https://zhuanlan.zhihu.com/p/30323085 这是原链接 ASCII编码 •ASCII编码大致可以分作三部分组成: •第一部分是:ASCII非打印控制字符(参详ASCII ...

  2. Robotframework学习笔记之—Rrobotframework运行报错“command: pybot.bat --argumentfile”

    Rrobotframework运行报错"command: pybot.bat --argumentfile" 解决方案: 1.可能是缺失文件: 1.1.检查python安装目录下的 ...

  3. STM32中的通信协议

    按照数据传送方式分: 串行通信(一条数据线.适合远距离传输)并行通信(多条数据线.成本高.抗干扰性差) 按照通信的数据同步方式分: 异步通信(以1个字符为1帧.发送与接收时钟不一致)同步通信(位同步. ...

  4. windows下命令

    shutdown -s -t 0 关机 shutdown -r -t 0 重启 mstsc 远程桌面 notepad 记事本 regedit 注册表 calc 计算器 start applicatio ...

  5. Js中的防抖与节流函数

    1.何为防抖与节流函数呢? 1.防抖(debounce):通过setTimeout方式,在一定的时间间隔内,将多次触发的事件转化为一次触发.也就是说当一个用户一直触发这个函数,且每次触发函数的间隔小于 ...

  6. 3.Java入门

    一.Java帝国的诞生 一场旷日持久的战争 1.C & C++ 1972年C诞生 贴近硬件(有汇编的一些特点),运行极快,效率极高 操作系统,编译器,数据库,网络系统等 指针(能够直接操作内存 ...

  7. Linux小白基础命令操作

    [root@localhost ~]]# [当前登录系统的用户@主机名称 当前所在的目录]# #表示为管理员登录 $ 表示为普通用户登录 切换用户su 用户名     切换后所在目录不变  ,#变成$ ...

  8. [源码解析] 深度学习分布式训练框架 horovod (16) --- 弹性训练之Worker生命周期

    [源码解析] 深度学习分布式训练框架 horovod (16) --- 弹性训练之Worker生命周期 目录 [源码解析] 深度学习分布式训练框架 horovod (16) --- 弹性训练之Work ...

  9. C语言:函数

    1. int scanf ( char * format [ ,argument, ... ]);   返回被赋值的参数的个数

  10. 三、从GitHub浏览Prism示例代码的方式入门WPF下的Prism之Mvvm的08-12示例

    这一篇是学习了前2篇RegionManager关联视图,和通过不同的方式加载Module示例之后的开始进入MVVM了. 从第08示例开始,进入了MVVM部分. 从08示例开始学习Prism下的MVVM ...