MZOI20200801

starseven

2020-08-01 16:50:00

Personal

[试题题目](https://www.maipdf.com/pdf/?email=jegki8Kj/FQIYz) ------------------------------ T1 这个就是一个裸的后缀数组和**最基本的height数组**运用。所以这道题就只是以后的模板复习题。 没什么值得说的,全都是套路。 $Code:$ ```cpp #include<cstdio> #include<algorithm> #define ri register int #define Starseven main const int N=2e4+20; int read(){ char ch=getchar();int re=0,op=1; while(ch<'0'||ch>'9'){if(ch=='-') op=-1;ch=getchar();} while(ch>='0'&&ch<='9'){re=(re<<3)+(re<<1)+ch-'0';ch=getchar();} return re*op; } void write(int x){ if(x<0){putchar('-');x=-x;} if(x>9) write(x/10); putchar(x%10+'0'); return ; }//记得自己加空格和换行 int max(int x,int y){return x<y?y:x;} int min(int x,int y){return x<y?x:y;} int abs(int x){return x<0?-x:x;} int n,tim,m; struct node{ int id,val,hsh; }txt[N]; bool cmp(const node &a,const node &b){ return a.val<b.val; } bool vast(const node &a,const node &b){ return a.id<b.id; } int tong[N],x[N],y[N],sa[N],rk[N],height[N]; struct xyx{ void Tsort(){ for(ri i=1;i<=m;i++) tong[i]=0; for(ri i=1;i<=n;i++) tong[x[i]]++; for(ri i=1;i<=m;i++) tong[i]+=tong[i-1]; } void Get_SA(){ for(ri i=1;i<=n;i++) x[i]=txt[i].hsh; Tsort(); for(ri i=n;i>=1;i--) sa[tong[x[i]]--]=i; for(ri k=1;k<=n;k<<=1){ int num=0; for(ri i=n-k+1;i<=n;i++) y[++num]=i; for(ri i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k; Tsort(); for(ri i=n;i>=1;i--) sa[tong[x[y[i]]]--]=y[i],y[i]=0; std::swap(x,y); num=1;x[sa[1]]=1; for(ri i=2;i<=n;i++) x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?num:++num); if(num==n) break; m=num; } } void Get_height(){ for(ri i=1;i<=n;i++) rk[sa[i]]=i; int k=0; for(ri i=1;i<=n;i++){ if(rk[i]==1) continue; if(k) k--; int j=sa[rk[i]-1]; while(j+k<=n&&i+k<=n&&txt[j+k].hsh==txt[i+k].hsh) k++; height[rk[i]]=k; } } }wly; int que[N]; void Init(){ freopen("1.in","r",stdin); } int Starseven(void){ // Init(); n=read(),tim=read(); for(ri i=1;i<=n;i++) txt[i].id=i,txt[i].val=read(); std::sort(txt+1,txt+1+n,cmp); txt[1].hsh=1; for(ri i=2;i<=n;i++) txt[i].hsh=(txt[i].val==txt[i-1].val?txt[i-1].hsh:txt[i-1].hsh+1); m=txt[n].hsh; std::sort(txt+1,txt+1+n,vast); wly.Get_SA();wly.Get_height(); int tail=-1,head=0,ans=0; for(ri i=1;i<=n;i++){ if(i-tim+1>=0) if(que[head]==i-tim+1) head++; while(tail>=head&&height[que[tail]]>=height[i]) tail--; que[++tail]=i; if(i-tim+1>=0) ans=max(ans,height[que[head]]); } write(ans);puts(""); return 0; } ``` ------------------------------------------------ T2 这是一个博弈论,而且是最基础(并不是最简单)的取石子游戏。但是有所变形,所以我猜测是$Fibonacci\;Nim$,只是有点变形,将$SG$函数弄出来找规律就直接出来了。 $Code:$ ```cpp #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<iostream> #include<map> #include<string> #define maxn 100005 #define INF (1ll<<60) using namespace std; inline long long getint() { long long num=0,flag=1;char c; while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1; while(c>='0'&&c<='9')num=num*10+c-48,c=getchar(); return num*flag; } long long k,N; long long f[maxn],fib[maxn],cnt; long long ans; inline void solve(int x) { if(k>f[x])ans++; for(int i=0;i<x;i++)if(k>f[i])ans+=fib[x-i]; } int main() { f[0]=0,f[1]=1,cnt=1,fib[0]=0,fib[1]=1; while(1) { cnt++; f[cnt]=f[cnt-1]+f[cnt-2]+1; fib[cnt]=fib[cnt-1]+fib[cnt-2]; if(f[cnt]>INF)break; } int T=getint(); while(T--) { k=getint(),N=getint();ans=0; if(N==1){printf("0\n");continue;} N-=2; for(int i=cnt;i>=0;i--)if(N>=f[i])solve(i),N-=f[i]+1; printf("%lld\n",ans); } } ``` --------------------------------- T3 这是一道倍增+树形换根dp的题,我没有做到,但是听懂了讲解,现在记录下来。 - 分析 我们对于一个$u->v$的路径,可以想到,我们在走这个路径的时候,在$u->v$的路径上的边只走一次,也就是单向,但是我们在走到一个节点时,可以选择**先绕道走一颗子树,然后在绕回来**的方案 #### 这提示我们要求出一个数组$f[i]$,以一个点为根的子树走一下能达到的最大收益(不一定要走完)。 然后我们还可以想象,当我们的两个节点走到其最近公共祖先$LCA(u,v)$时,我们可以向上走,走了可以走到的最大收益过后再回来,然后在走。 #### 这提示我们要求出一个数组$g[i]$,代表从这个点向上走,走到最大收益后回来所得到的收益 $\text{\color{red}{But,one\;thing\;important\;is\dots}}$ 我们在以一条链向上走的时候,难道运用$f[i]$一个一个求,那不就是暴力的$O(n\times q)$的做法吗,所以我们还要继续想。 看到是树上的$LCA$一类的东西,自然而然地想到了**树链剖分**(~~假装自己是个树剖dalao~~)或者倍增来统计自己的答案。 统计什么答案呢? #### 按照倍增的思想,我们每一次倍增都是得到的一条链的首尾,所以我们可以求出一个$F[i][j]$数组,表示从i点向上跳$2^j$个深度,这条链所能作出的最大贡献 按照前面的思想,而这个链当然不需要来回,但是进入子树需要。 #### 而我们之前的$f[i]$数组就起到辅助的作用了 而既然求出了向上的,我们当然要求出向下的。 $\text{设F2[i][j]为点}(i-2^j)\text{到点}(i)\text{所需能得到的最大收益}$ 然后就可以写代码了。 -------------------------- 细节很多,逐个逐个理解数组的含义 ```cpp #include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <cmath> #define maxn 200005 #define R register #define INF1 0x3f3f3f3f using namespace std; typedef long long lxl; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return x*f; } struct edge { int v,w,next; }e[maxn<<1]; int head[maxn],k; inline void add(int u,int v,int w) { e[k]=(edge){v,w,head[u]}; head[u]=k++; } int n,q,a[maxn]; int f[maxn]; inline void dp(int u,int fa) { f[u]=a[u]; for(int i=head[u];~i;i=e[i].next) { int v=e[i].v; if(v==fa) continue; dp(v,u); if(f[v]-e[i].w-e[i^1].w>0) f[u]+=f[v]-e[i].w-e[i^1].w; } } int p[maxn][30],F1[maxn][30],F2[maxn][30],g[maxn]; int dep[maxn]; inline void dfs(int u,int fa) { dep[u]=dep[p[u][0]=fa]+1; for(int i=1;i<=25;++i) { p[u][i]=p[p[u][i-1]][i-1]; F1[u][i]=F1[u][i-1]+F1[p[u][i-1]][i-1]-f[p[u][i-1]]; F2[u][i]=F2[u][i-1]+F2[p[u][i-1]][i-1]-f[p[u][i-1]]; } for(int i=head[u];~i;i=e[i].next) { int v=e[i].v; if(v==fa) continue; F1[v][0]=f[v]+f[u]-e[i^1].w; if(f[v]-e[i].w-e[i^1].w>0) F1[v][0]-=f[v]-e[i].w-e[i^1].w; F2[v][0]=f[v]+f[u]-e[i].w; if(f[v]-e[i].w-e[i^1].w>0) F2[v][0]-=f[v]-e[i].w-e[i^1].w; g[v]=max(g[v],g[u]+F1[v][0]-e[i].w-f[v]); dfs(v,u); } } inline int Query(int u,int v) { int ans=0,a=u,b=v; if(dep[u]>dep[v]) { for(int i=25;i>=0;--i) if(dep[p[u][i]]>=dep[v]) { ans+=F1[u][i]-f[u]; u=p[u][i]; } if(u==v) return ans+f[a]+g[u]; } else { for(int i=25;i>=0;--i) if(dep[p[v][i]]>=dep[u]) { ans+=F2[v][i]-f[v]; v=p[v][i]; } if(u==v) return ans+f[b]+g[u]; } for(int i=25;i>=0;--i) if(p[u][i]!=p[v][i]) { ans+=F1[u][i]-f[u]; ans+=F2[v][i]-f[v]; u=p[u][i]; v=p[v][i]; } return ans+F1[u][0]-f[u]+F2[v][0]-f[v]-f[p[u][0]]+f[a]+f[b]+g[p[u][0]]; } int main() { freopen("strawberry2.in","r",stdin); freopen("c.out","w",stdout); n=read(),q=read(); for(int i=1;i<=n;++i) a[i]=read(); memset(head,-1,sizeof(int)*(n+5)); for(int i=1;i<n;++i) { int u=read(),v=read(); add(u,v,read()); add(v,u,read()); } dp(1,0); dfs(1,0); while(q--) { int u=read(),v=read(); printf("%d\n",Query(u,v)); } return 0; } ```