辣鸡(lijh)mikufun考试以后就颓废了,然后就滚去颓了
这次第一题看了半天也只有n^2算法,码了半天才调出来(分类讨论吃屎),然后想出来个优化,码上之后又想了20min,实在没思路就扔了(竟然A了)
T2完全没有优秀复杂度的思路(其实是没看数据范围),打了个最坏O(n^2logn)的算法期望骗分,30分GG
T3。。。。。不看题*2,完全看错题,只过了n==1的点,5分
总分100+30+5=135pts,rank6,还行吧,以后努力
T1:辣鸡(lijh)
没什么可说的,直接枚举两个块是否相邻就好了
T2:模板(ac)
get新姿势:树上启发式合并,rp+
简要说一下树上启发式合并:对于要进行的统计,直接n^2难以卡过,但是对于一颗树来说,儿子的信息是可以传到父亲的,然而儿子之间会相互影响
所以我们考虑这样一个思路:每次只保留一个儿子的信息,那么保留哪个呢?显然我们应该找最大的那个儿子(称为重儿子),然后其他的儿子暴力插入这段信息里
这样我们可以只种一棵线段树,如果当前处理的是重儿子就保留线段树,否则直接清空,注意清空的时候不能memset(no memset too long),否则复杂度直接退化
处理出一个vis数组,vis[x]表示颜色x在当前情况下最早插入时间
如果处理完一个节点的所有儿子,这时已经得到了重儿子的线段树和vis数组,我们再遍历他的所有轻儿子,把轻儿子上的操作暴力插入这棵线段树
如果对于当前插入的颜色,在他之前已经有相同颜色插入,那贡献为零
如果在他之后有相同颜色插入,在vis[x]位置贡献-1,位置贡献+1,并更新vis
如果没有过相同颜色插入,直接贡献++
这样我们得到了当前子树的插入所构成的线段树,然后在线段树里二分查找(和平衡数求k大一样)就能得到当前点的答案
然后删除的时候,遍历整棵子树并把vis置为最大值,线段树可以打成指针,这样指针池可以直接清空
还有,如果不像lnc那样将vector上传,而是像我一样遍历所有轻儿子的话,sz既不能是子树大小也不能是插入数大小,而应该是他们的和。。。。。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h>
2 using namespace std;
3 #define lc k->ls,l,mid,st,x,y
4 #define rc k->rs,mid+1,r,st,x,y
5 #define mp make_pair
6 #define pb push_back
7 #define ci const int
8 ci maxn=19260817;
9 int pre[100010],wei,ma,son[100010],ans[100010],sz[100010],can[100010],n,m;
10 vector< pair<int,int> >q[100010];
11 map<int,int>p;
12 int tot,num,fa[100010],to[200010],la[200010],v[100010];
13 inline void add(ci x,ci y){
14 to[++num]=y;
15 la[num]=fa[x];
16 fa[x]=num;
17 }
18 struct node{
19 node *ls,*rs;
20 int da,is;
21 }*rt,pool[400010];
22 inline node *New(){
23 ++tot;
24 pool[tot].ls=pool[tot].rs=NULL;
25 pool[tot].is=pool[tot].da=0;
26 return &pool[tot];
27 }
28 void ads(node *&k,ci l,ci r,ci st,ci x,ci y){
29 if(k==NULL) k=New();
30 if(l==r){
31 k->da+=x;
32 k->is+=y;
33 return;
34 }
35 int mid=l+r>>1;
36 if(st<=mid) ads(lc); else ads(rc);
37 k->da=(k->ls!=NULL?k->ls->da:0)+(k->rs!=NULL?k->rs->da:0);
38 k->is=(k->ls!=NULL?k->ls->is:0)+(k->rs!=NULL?k->rs->is:0);
39 }
40 int query(node *k,ci l,ci r,ci dd){
41 if(!dd||k==NULL) return 0;
42 if(k->da<=dd) return k->is;
43 int mid=l+r>>1;
44 if(k->ls!=NULL&&k->ls->da>dd) return query(k->ls,l,mid,dd);
45 else if(k->rs!=NULL) return (k->ls!=NULL?k->ls->is:0)+query(k->rs,mid+1,r,dd-(k->ls!=NULL?k->ls->da:0));
46 }
47 void sumadd(ci u,ci ff,ci o){
48 for(int i=0;i<q[u].size();i++){
49 int t=q[u][i].first,tt=q[u][i].second;
50 if(o) pre[t]=maxn;
51 else{
52 if(pre[t]<tt) ads(rt,1,m,tt,1,0);
53 else{
54 ads(rt,1,m,tt,1,1);
55 if(pre[t]!=maxn)ads(rt,1,m,pre[t],0,-1);
56 pre[t]=tt;
57 }
58 }
59 }
60 for(int i=fa[u];i;i=la[i])
61 if(to[i]!=ff&&!v[to[i]])
62 sumadd(to[i],u,o);
63 }
64 void dfs(ci u,ci ff,ci t){
65 rt=NULL;
66 for(int i=fa[u];i;i=la[i])
67 if(to[i]!=ff&&to[i]!=son[u]) dfs(to[i],u,0);
68 if(son[u]) dfs(son[u],u,1),v[son[u]]=1;
69 sumadd(u,ff,0);
70 ans[u]=query(rt,1,ma,can[u]);
71 if(son[u]) v[son[u]]=0;
72 if(!t) sumadd(u,ff,1),tot=0,rt=NULL;
73 }
74 void gts(ci u,ci ff){
75 sz[u]=1+q[u].size();
76 for(int i=fa[u];i;i=la[i])
77 if(to[i]!=ff){
78 gts(to[i],u);
79 if(sz[to[i]]>sz[son[u]]) son[u]=to[i];
80 sz[u]+=sz[to[i]];
81 }
82 }
83 int main(){
84 int x,y,Q;
85 scanf("%d",&n);
86 for(int i=1;i<n;i++){
87 scanf("%d%d",&x,&y);
88 add(x,y); add(y,x);
89 }
90 for(int i=1;i<=n;i++) scanf("%d",&can[i]);
91 scanf("%d",&m);
92 for(int i=1;i<=m;i++){
93 scanf("%d%d",&x,&y);
94 if(!p[y]) p[y]=++ma,pre[ma]=maxn;
95 y=p[y];
96 q[x].pb(mp(y,i));
97 }
98 gts(1,0); dfs(1,0,1);
99 scanf("%d",&Q);
100 for(int i=1;i<=Q;i++){
101 scanf("%d",&x);
102 printf("%d
",ans[x]);
103 }
104 }
T3:大佬(kat)
一道假期望水题,考察点循环+快速幂
只考虑每种难度的贡献,在长度为k的区间中最大难度为i的方案数为i^k-(i-1)^k,再考虑有多少个这样的区间,求和,最后除以一共有几种区间就好了
以上
我待曙色沾霜,才知南柯一场