T1:林下风气

Problem:

里口福因有林下风气,带领全国各地高校掀起了一股 AK 风,大家都十分痴迷于 AK。里口福为了打击大家的自信心,出了一道自以为十分困难的题目。

里口福有一棵树,第 i 个节点上有点权 ai,他的问题就是这棵树中有多少个不同的连通块满足连通块的最大值与最小值之差=k,两个连通块不同当且仅当至少存在一个节点在一个连通块中出现而另一个连通块中没有出现。
第一行两个整数 n,k,表示树的大小以及题目中的 k。
第二行 n 个整数,第 i 个整数表示 ai。
接下来 n-1 行,每行两个整数 x,y 表示树边(x,y)。

Solution:

  • 算法 1
2N枚举每个点选或不选,以枚举所有的连通块,再暴力判断是否满足题目要求即可。
时间复杂度O(2n),期望得分30。
  • 算法 2
输出0/1/n+2均可获得 10 分,配合随机化(或打点)甚至可能获得30分的好成绩,期望得分10 至30不等。
  • 算法 3
树是一条链,把链看成一个序列,那么一个连通块必定对应必定序列上连续的一段区间,枚举左端点,在从左到右枚举右端点,同时记录下最大值与最小值,倘若最大值与最小值差=k,那么答案加 1,时间复杂度O(n2),期望得分20,结合算法 1 可以拿到50分的好成绩。
  • 算法 4
当全部 a 都为 0 或 1 时,可以发现只有当 k=0/1 时答案不为0。若 k=0,则说明只有当一个连通块中的点全部都相同时才可行,设݂݅݅ fi 表示从 i 以为根的子树中选出包含 i 且与 i 颜色相同的合法连通块的数量,转移比较简单。若k=1,则用所有的可能情况减去 k=0 的情况即可。时间复杂度 O(n),期望得分20,结合算法 1/3 可以拿到70分的好成绩。
  • 算法 5
考虑树形dp。
我们将一个可行的连通块的贡献记在该连通块深度最小的点上,这样便可以做到不重不漏。
O(n)枚举合法连通块的最小值,那么便有唯一的最大值与之对应。
设 fi,0/1,0/1 表示从以 i 为根的子树中选出包含 i,最小值是否出现以及最大值是否出现的连通块的数量,枚举后两维的状态暴力转移即可,注意判断不可行的情况。
时间复杂度 O(n2),常数较大,期望得分100。

Code:

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 const int mod=19260817;
 5 const int maxn=1100000;
 6 int n,k,u,v,tot,a[maxn];
 7 int head[maxn],nxt[maxn],to[maxn];
 8 inline void add(int u,int v){
 9      tot++;
10      to[tot]=v;
11      nxt[tot]=head[u];
12      head[u]=tot;
13 }
14 inline int dp(int now,int fa,int r,int k){
15      if(k<0) return 0;
16      int mid=1;
17      for(int i=head[now];i;i=nxt[i]){
18           int y=to[i];
19           if(y==fa) continue;
20           if(a[y]>=a[r]&&a[y]<=a[r]+k&&(a[y]!=a[r]||a[y]==a[r]&&y<r)){
21               mid=mid*(dp(y,now,r,k)+1)%mod;
22           }
23      }
24      return mid;
25 }
26 signed main(){
27     cin>>n>>k;
28     for(int i=1;i<=n;i++){
29         cin>>a[i];
30     }
31     for(int i=1;i<n;i++){
32         cin>>u>>v;
33         add(u,v);
34         add(v,u);
35     }
36     int ans=0;
37     for(int i=1;i<=n;i++){
38         ans=(ans+((dp(i,0,i,k)-dp(i,0,i,k-1))%mod+mod)%mod)%mod;
39     }
40     cout<<ans<<endl;
41     return 0;
42 }

T2:盟主的忧虑

Problem:

江湖由 N 个门派(2≤N≤100,000,编号从 1 到 N)组成,这些门派之间有 N-1 条小道将他们连接起来,每条道路都以“尺”为单位去计量,武林盟主发现任何两个门派都能够直接或者间接通过小道连接。

虽然整个江湖是可以互相到达的,但是他担心有心怀不轨之徒破坏这个武林的安定,破坏小道,于是武林盟主又秘密地修建了 M 条密道(1≤M≤100,000),但每条小道距离都不超过 10 亿尺。
果不其然,最近一个名叫“太吾”的组织意欲破坏武林的小道,请你帮盟主想想办法,如果门派 A 到门派 B 的直连小道被破坏,从 A 走到 B 的所有路径中,经过密道的距离最少是多少?
第一行数字N M
接下来N-1行,每行两个整数A,B,表示A-B间有一条直连小道
接下来M行,每行三个数字 A,B,V,表示A-B间有一条代价为 V 的密道

Solution:

  • N<=1000,M<=1000
枚举哪条边是去掉的,然后在剩下的图里面跑最短路(dij或spfa)。
时间复杂度O(N(N+M) log N)
  • N<=5000,M<=5000
容易发现,去掉一条树边之后,经过的密道至多只有一条,对于一条密道(u,v,w),在u到v的路径上的边的答案至多为w,暴力更新即可。
时间复杂度O(N M)
  • 树是一条链
在上一档部分分中,我们知道,相当于对树中路径做M次的路径的覆盖。
在一条链的情况下,每次相当于对一个区间上的所有元素取min,用线段树维护即可。
时间复杂度O(N log N)
  • N,M<=100000
将所有密道按照权值从小到大排序。
对于一条密道(u,v,w),如果u到v的路径上的边中存在边之前还没有被覆盖过,那么说明这条边的答案就是w.
可以用并查集维护,维护每个集合深度最小的节点,对于密道(u,v,w),每次u都在它所在集合中找到深度最小的点,这个点与父亲的连边一定就是上述的边,将这条边的答案更新为w,然后将这个节点与其父亲合并,直到u所在集合的深度最小的节点是小于u和v的lca的,对v做同样的过程即可。
时间复杂度O(Ma(N))

Code:

  1 #include<bits/stdc++.h>
  2 #define int long long
  3 #define ls x<<1
  4 #define rs x<<1|1
  5 using namespace std;
  6 const int maxn=100005;
  7 int n,m,vis[maxn],pre[maxn],ans[maxn],use[maxn];
  8 int cnt,son[maxn],se[maxn],be[maxn],dep[maxn],top[maxn];
  9 int tot=1,head[maxn],nex[maxn<<1],to[maxn<<1],fa[maxn];
 10 struct node{
 11     int l,r,mid,id;
 12 }t[1000005];
 13 inline int read(){
 14     int x=0,f=1;
 15     char ch=getchar();
 16     while(ch<'0'||ch>'9'){
 17         if(ch=='-') f=-1;
 18         ch=getchar();
 19     }
 20     while(ch>='0'&&ch<='9'){
 21         x=(x<<3)+(x<<1)+ch-'0';
 22         ch=getchar();
 23     }
 24     return x*f;
 25 }
 26 inline void add(int x,int y){
 27     to[++tot]=y;
 28     nex[tot]=head[x];
 29     head[x]=tot;
 30 }
 31 inline void dfs1(int x,int f){
 32     se[x]=1;
 33     dep[x]=dep[f]+1;
 34     fa[x]=f;
 35     for(int i=head[x];i;i=nex[i]){
 36         int y=to[i];
 37         if(y==f) continue;
 38         se[x]+=1;
 39         dfs1(y,x);
 40         if(se[y]>se[son[x]]) son[x]=y;
 41         pre[y]=i/2;
 42     }
 43 }
 44 inline void dfs(int x,int tv){
 45     cnt+=1;
 46     be[x]=cnt;
 47     top[x]=tv;
 48     if(son[x]) dfs(son[x],tv);
 49     for(int i=head[x];i;i=nex[i]){
 50         int y=to[i];
 51         if(y==fa[x]||y==son[x]) continue;
 52         dfs(y,y);
 53     }
 54 }
 55 inline void push_down(int x){
 56     if(t[x].id==1){
 57         if(t[ls].id==0) t[ls].mid=t[x].mid;
 58         else t[ls].mid=min(t[ls].mid,t[x].mid);
 59         if(t[rs].id==0) t[rs].mid=t[x].mid;
 60         else t[rs].mid=min(t[rs].mid,t[x].mid);
 61         t[ls].id=1;
 62         t[rs].id=1;
 63     }
 64 }
 65 inline void build(int x,int l,int r){
 66     t[x].l=l;
 67     t[x].r=r;
 68     t[x].mid=0;
 69     t[x].id=0;
 70     if(l==r) return ;
 71     int mi=(l+r)/2;
 72     build(ls,l,mi);
 73     build(rs,mi+1,r);
 74 }
 75 inline void insert(int x,int L,int R,int k){
 76     int l=t[x].l,r=t[x].r;
 77     push_down(x);
 78     if(L<=l&&R>=r){
 79         if(t[x].id==0) t[x].mid=k;
 80         else t[x].mid=min(t[x].mid,k);
 81         t[x].id=1;
 82         return ;
 83     }
 84     int mid=(l+r)/2;
 85     if(L<=mid) insert(ls,L,R,k);
 86     if(R>mid) insert(rs,L,R,k);
 87 }
 88 inline int query(int x,int pos){
 89     int l=t[x].l,r=t[x].r;
 90     push_down(x);
 91     if(l==r){
 92         if(t[x].id==0) return -1;
 93         else return t[x].mid;
 94     }
 95     int mid=(l+r)/2,ans=0;
 96     if(mid>=pos) ans=query(ls,pos);
 97     else ans=query(rs,pos);
 98     return ans;
 99 }
100 inline void solve(int x,int y,int z){
101     while(top[x]!=top[y]){
102         if(dep[top[x]]<dep[top[y]]) swap(x,y);
103         insert(1,be[top[x]],be[x],z);
104         x=fa[top[x]];
105     }
106     if(x==y) return ;
107     if(dep[x]<dep[y]) swap(x,y);
108     insert(1,be[y]+1,be[x],z);
109 }
110 signed main(){
111     n=read();
112     m=read();
113     for(int i=1;i<n;i++){
114         int x,y;
115         x=read();
116         y=read();
117         add(x,y);
118         add(y,x);
119     }
120     build(1,1,n);
121     dfs1(1,0);
122     dfs(1,1);
123     for(int i=1;i<=m;i++){
124         int x,y,z;
125         x=read();
126         y=read();
127         z=read();
128         solve(x,y,z);
129     }
130     for(int i=1;i<=n;i++){
131         ans[pre[i]]=query(1,be[i]);
132     }
133     for(int i=1;i<=n-1;i++){
134         cout<<ans[i]<<"\n";
135     }
136     return 0;
137 }

T3:明日之星

Problem:

n 位舞台少女各自有一个番号,番号是由‘A’、‘C’、‘G’、‘T’、‘U’五种字符组成的字符串,某种未知力量导致不同的舞台少女的番号可能相同。
我们把第 i 位舞台少女的番号记作 s[i],且每位舞台少女还会有一个梦想值a[i]。
舞台少女之间互相建立了友好的关系,如果把关系看作边,那么这是一棵无根树。
giraffe 想了一种奇特的点名方式,每次点名它会有一个名单 S,S 也是由‘A’、‘G’、‘C’、‘T’、‘U’五种字符组成的字符串,然后它从第 u 位舞台少女走最短路到第 v 位舞台少女,对于途中经过的每位舞台少女 x(包括 u、v),x 的分数为 番号 s[x]在 S 中出现的次数 * 梦想值 a[x],giraffe 想知道分数之和。
当然,舞台少女们随着心情的变化梦想值也是会改变的。
giraffe:“I see.”
谁也不知道 giraffe 知道的分数和是多少,所以拜托你了。
由于 gireffe 喜欢未知的舞台,所以本题强制在线。
第一行两个正整数 n,tp,表示一共有 n 位舞台少女,tp 是强制在线参数,tp=0 或 1。
接下来 n 行,每行一个非空字符串 s[i],表示第 i 位舞台少女的番号,s[i]由‘A’、‘G’、‘C’、‘T’、‘U’五种字符组成。
接下来一行 n 个正整数,第 i 个整数 a[i]表示第 i 位少女一开始的梦想值。
再接下来 n-1 行,每行两个数 x,y,表示第 x 位舞台少女和第 y 位舞台少女有友好的关系。
一行一个整数 Q,表示一共有 Q 次询问或修改。
最后 Q 行,每一行第一个整数 op,表示操作类型。
若 op=1,表示这次操作为询问,接下来 2 个数u_,v_,真正的u=u_xor(lastans*tp),v=v_ xor (lastans*tp),和一个字符串 S,含义如题。
若 op=2,表示这次操作为修改,接下来两个数x_,c_,真正的x=x_xor(lastans*tp),c=c_ xor(lastans*tp),表示第 x 位舞台少女梦想值变为 c 了。
lastans 表示上一次询问的答案,初值为 0.

Solution:

图论、数据结构、字符串综合题
  • 1
随机树下路径长度期望是log n的,显然直接暴力把路径上的点全部弄出来KMP就行了。
时间复杂度O(|S|*log n)
  • 2
树是链,查询只是1->n,且没有修改。
那么先把所有的串s建个AC自动机,预处理AC自动机上每个点往fail链跳会遇到多少个结尾的点,然后对于每个询问丢进AC自动机跑就好了。
复杂度O(|s|+|S|)
  • 3
树是链,查询只是1->n,有修改
还是建出AC自动机,AC自动机fail反链会形成一棵树。对于每个修改相当于把对应的结尾的点的fail反链树下的子树加(减)一个数。
于是求出fail反链树的dfs序,那么子树会在一个区间里,树状数组维护,复杂度O((Q + |S|)log |s|)
  • 4
树是链,查询任意,无修改。
做法1:对于2号点的做法直接加上一个不带修主席树。
做法2:用线段树分治结构去维护AC自动机,即线段树每个点代表一个区间,把这个区间里的点代表的串建AC自动机,然后和2号点的做法一样
复杂度都是一个log的。
  • 5
树是链,查询任意,有修改,强制在线。
结合3、4号点的做法可以得到两个做法:
1.带修主席树
2.线段树分治结构建AC自动机+树状数组
复杂度都是log^2
发现5号点的做法1本质是维护三维偏序,所以不强制在线的话也可以cdq分治
100:结合5号点算法2即可得到满分。

Code:

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 const int maxn=2e5+5;
  4 const int maxm=4e5+5;
  5 const int maxp=1e7;
  6 int n,tp,Q,op,x,y,a[maxn],w[18][maxn],dep[maxn];
  7 char str[maxm],s[maxm];
  8 int len,ls[maxn],rs[maxn];
  9 struct node{
 10     int head[maxn],to[maxn*2],nxt[maxn*2],tot;
 11     void add(int x,int y){
 12         nxt[++tot]=head[x];
 13         to[tot]=y;
 14         head[x]=tot;
 15     }
 16 }e;
 17 struct node2{
 18     int head[maxp],to[maxm],nxt[maxm],tot;
 19     void add(int x,int y){
 20         nxt[++tot]=head[x];
 21         to[tot]=y;
 22         head[x]=tot;
 23     }
 24 }e2;
 25 struct node3{
 26     int x,y;
 27 }b[maxn*2];
 28 int p[maxn],q[maxn],tt;
 29 int ch(char c){
 30     if(c=='A') return 0;
 31     if(c=='G') return 1;
 32     if(c=='C') return 2;
 33     if(c=='T') return 3;
 34     return 4;
 35 }
 36 inline int lowbit(int x){
 37     return x&-x;
 38 }
 39 inline void dg(int x){
 40     dep[x]=dep[w[0][x]]+1;
 41     p[x]=++tt;
 42     b[tt].x=x;
 43     b[tt].y=1;
 44     for(int i=e.head[x];i;i=e.nxt[i]){
 45         if(!p[e.to[i]]){
 46             w[0][e.to[i]]=x;
 47             dg(e.to[i]);
 48         }
 49     }
 50     q[x]=++tt;
 51     b[tt].x=x;
 52     b[tt].y=-1;
 53 }
 54 inline int lca(int x,int y){
 55     if(dep[x]<dep[y]) swap(x,y);
 56     for(int i=17;i>=0;i--){
 57         if(dep[w[i][x]]>=dep[y]){
 58             x=w[i][x];
 59         }
 60     }
 61     if(x==y) return x;
 62     for(int i=17;i>=0;i--){
 63         if(w[i][x]!=w[i][y]){
 64             x=w[i][x];
 65             y=w[i][y];
 66         }
 67     }
 68     return w[0][x];
 69 }
 70 int to[maxp][5],fail[maxp],tc,g[maxn*8],G[maxn*8],d[maxm];
 71 int ed[20][maxn*2],p2[maxp],q2[maxp],pp;
 72 inline void dd(int x){
 73     p2[x]=++pp;
 74     for(int i=e2.head[x];i;i=e2.nxt[i]){
 75         dd(e2.to[i]);
 76     }
 77     q2[x]=pp;
 78 }
 79 int ans,f[maxp];
 80 inline void bt(int i,int x,int y,int dp){
 81     g[i]=++tc;
 82     for(int j=x;j<=y;j++){
 83         int p=g[i];
 84         for(int k=ls[b[j].x];k<=rs[b[j].x];k++){
 85             if(!to[p][s[k]]) to[p][s[k]]=++tc;
 86             p=to[p][s[k]];
 87         }
 88         ed[dp][j]=p;
 89     }
 90     G[i]=tc;
 91     for(int j=1;j<=e2.tot;j++){
 92         e2.nxt[j]=0;
 93     }
 94     e2.tot=0;
 95     for(int j=0;j<=4;j++){
 96         to[0][j]=g[i];
 97     }
 98     d[d[0]=1]=g[i];
 99     for(int j=1;j<=d[0];j++){
100         int x=d[j];
101         if(j>1) e2.add(fail[x],x);
102         for(int k=0;k<=4;k++){
103             if(to[x][k]){
104                 int y=to[x][k],p=fail[x];
105                 while(!to[p][k]) p=fail[p];
106                 fail[y]=to[p][k];
107                 d[++d[0]]=y;
108             }
109         }
110     }
111     dd(g[i]);
112     for(int j=x;j<=y;j++){
113         f[p2[ed[dp][j]]]+=a[b[j].x]*b[j].y;
114         if(q2[ed[dp][j]]<G[i]){
115             f[q2[ed[dp][j]]+1]-=a[b[j].x]*b[j].y;
116         }
117     }
118     int q=G[i]-g[i]+1;
119     for(int j=1;j<=q;j++){
120         if(j+lowbit(j)<=q){
121             f[g[i]+j+lowbit(j)-1]+=f[g[i]+j-1];
122         }
123     }
124     if(x==y) return ;
125     int mid=(x+y)>>1;
126     bt(i<<1,x,mid,dp+1);
127     bt(i<<1|1,mid+1,y,dp+1);
128 }
129 int pl,pr,px;
130 inline void add(int x,int n,int st,int c){
131     x-=--st;
132     while(x+st<=n){
133         f[x+st]+=c;
134         x+=lowbit(x);
135     }
136 }
137 inline void ff(int x,int st){
138     x-=--st;
139     while(x){
140         ans+=f[x+st];
141         x-=lowbit(x);
142     }
143 }
144 inline void xiu(int i,int x,int y,int dp){
145     if(y<pl||x>pr) return ;
146     add(p2[ed[dp][pl]],G[i],g[i],px);
147     add(q2[ed[dp][pl]]+1,G[i],g[i],-px);
148     if(x==y) return ;
149     int mid=(x+y)>>1;
150     xiu(i<<1,x,mid,dp+1);
151     xiu(i<<1|1,mid+1,y,dp+1);
152 }
153 char t[maxm];
154 int lt;
155 inline void fi(int i,int x,int y,int dp){
156     if(y<pl||x>pr) return ;
157     if(x>=pl&&y<=pr){
158         int p=g[i];
159         for(int j=0;j<=4;j++){
160             to[0][j]=p;
161         }
162         for(int j=1;j<=lt;j++){
163             while(!to[p][t[j]]) p=fail[p];
164             p=to[p][t[j]];
165             ff(p2[p],g[i]);
166         }
167         return ;
168     }
169     int mid=(x+y)>>1;
170     fi(i<<1,x,mid,dp+1);
171     fi(i<<1|1,mid+1,y,dp+1);
172 }
173 int main(){
174     scanf("%d%d",&n,&tp);
175     for(int i=1;i<=n;i++){
176         scanf("%s",str+1);
177         len=strlen(str+1);
178         ls[i]=rs[i-1]+1;
179         rs[i]=rs[i-1]+len;
180         for(int j=ls[i];j<=rs[i];j++){
181             s[j]=ch(str[j-ls[i]+1]);
182         }
183     }
184     for(int i=1;i<=n;i++){
185         scanf("%d",&a[i]);
186     }
187     for(int i=1;i<=n-1;i++){
188         scanf("%d%d",&x,&y);
189         e.add(x,y);
190         e.add(y,x);
191     }
192     dg(1);
193     for(int i=1;i<=17;i++){
194         for(int j=1;j<=n;j++){
195             w[i][j]=w[i-1][w[i-1][j]];
196         }
197     }
198     bt(1,1,tt,0);
199     scanf("%d",&Q);
200     for(int ii=1;ii<=Q;ii++){
201         scanf("%d%d%d",&op,&x,&y);
202         x^=ans*tp;
203         y^=ans*tp;
204         if(op==1){
205             scanf("%s",t+1);
206             lt=strlen(t+1);
207             for(int j=1;j<=lt;j++){
208                 t[j]=ch(t[j]);
209             }
210             int z=lca(x,y);
211             ans=0;
212             pl=p[z];
213             pr=p[x];
214             fi(1,1,tt,0);
215             pl=p[z]+1;
216             pr=p[y];
217             fi(1,1,tt,0);
218             printf("%d\n",ans);
219         }
220         else{
221             pl=pr=p[x];
222             px=y-a[x];
223             xiu(1,1,tt,0);
224             pl=pr=q[x];
225             px=-px;
226             xiu(1,1,tt,0);
227             a[x]=y;
228         }
229     }
230     return 0;
231 }