T348139 E-挑战(Challenge)题解
前言
不写部分分了,直奔主题
writen by yjy
注:接下来时间及空间复杂度的 \(n\) 都不妨令它与题目中给出的 \(n,m,Q\) 同阶。
problem 1:
觉得把 \(p_i,q_j\) 写成 \(a_i,b_j\) 更美观点,下面令:\(a_i=p_i,b_j=q_j.\)
- 求和的询问:我们观察到 \(\forall_{x_0 \le i \le x_1},\)矩阵在第 \(i\) 行的和为:\(\Sigma_{j=y_0}^{y_1}(a_i+b_j)=\Sigma_{j=y_0}^{y_1}b_j+(y_1-y_0+1)a_i.\)
令 \(ss=\Sigma_{j=y_0}^{y_1}b_j,tt=\Sigma_{i=x_0}^{x_1}a_i\),那么把矩阵每行的和加起来就得到答案为:\(\Sigma_{i=x_0}^{x_1}[ss+(y_1-y_0+1)a_i]=ss(x_1-x_0+1)+tt(y_1-y_0+1).\)
\(ss,tt\) 可以用前缀和维护,注意取模。
-
最大值的询问:根据贪心的思想,显然最大值在 \(a_i\) 最大且 \(b_j\) 最大的一行上取到(\(i,j\) 的取值范围不再重申)。所以我们只需用数据结构维护区间最大值即可,最后查询时分别求出区间上 \(a,b\) 两序列的最大值,再相加即得矩阵内最大值。
-
最大公约数的询问:PS:以下的 \(b_{j+1}-b_j,a_{l+1}-a_l\) 等都为正数(严格表示的话,应该给每一个都加上绝对值,不然运行会得出错误的结果,作者下面就偷点懒)。
先考虑第 \(i\) 行的任意子段 \([j,k]\) 最大公约数,可用辗转相除法变换一下,令:\(u_i=gcd(a_i+b_j,a_i+b_{j+1},...a_i+b_k)=gcd(a_i+b_j,b_{j+1}-b_j,...b_{k-1}-b_{k-2},b_k-b_{k-1})\)。
然后把第 \(l\) 到 \(r\) 行的 \(u_i\) 再做个最大公约数,即为单次询问的答案(我们可以发现每个 \(u_i\) 有很大一部分是相同的):\(ans=gcd(u_l,u_{l+1}...u_r)=gcd(gcd(a_l+b_j,a_{l+1}+b_j...a_r+b_j),gcd(b_{j+1}-b_j,...b_{k-1}-b_{k-2},b_k-b_{k-1}))\)。
再对第 \(j\) 列的那一段最大公约数采用求 \(u_i\) 的方法,可得:\(v_j=gcd(a_l+b_j,a_{l+1}+b_j...a_r+b_j)=gcd(a_l+b_j,a_{l+1}-a_l,a_{l+2}-a_{l+1}...a_r-a_{r-1})\)。
\(ans=gcd(a_l+b_j,gcd(a_{l+1}-a_l,a_{l+2}-a_{l+1}...a_r-a_{r-1}),gcd(b_{j+1}-b_j,...b_{k-1}-b_{k-2},b_k-b_{k-1})).\)
很显然,\(a_l+b_j\) 我们可以 \(O(1)\) 求得,而后面的两个式子都对应一个子段,所以我们可以用数据结构(如线段树,ST 表等)来维护,复杂度为 \(O(n \log n)\)。这足以通过。
综上,problem 1 的时间及空间复杂度均为 \(O(n \log n)\).
problem 2:
由于强制在线,这样就可以卡掉一些玄学做法所以我们得用点支持在线操作的数据结构。
正解为:可持久化线段树。
相信读到这里的 OIer 一些基本的能力是很充足的,具体实现方法就写的简略点。
不妨令 1 为根节点,考虑对于树上每个结点建主席树,记录从结点 1 到它的路径上所有点的权值。其中结点 \(i\) 的这棵主席树上,某个结点若对应区间 \([l,r]\),那么它的值就表示:从结点 1 到 \(i\) 的路径上权值 \(\in [l,r]\) 的结点个数。
我们需要保证 \(O(n \log n)\) 的复杂度,就不能直接弄到 dfs 序上,去树链剖分+主席树,否则就会多一只 \(log\)。我们观察到树上两点间路径上的权值可以通过 树上差分 来维护,即结点 \(u,v\) 间可通过:\(u,v,lca(u,v),fa_{lca(u,v)}\) 四个结点对应的主席树差分得到值。
到这一步后,三种不同的问题分别写个询问就好了。
Code(std):
//std,writen by yjy
#include<bits/stdc++.h>
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define inf 0x7fffffff
#define v e[i].y
using namespace std;
inline ll read(){
char ch=getchar();ll x=0,w=1;
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-48,ch=getchar();return w==1?x:-x;
}
inline void write(ll x){
if(x<0)x=-x,putchar('-');
if(x<10){putchar(48+x);return;}
write(x/10),putchar((x+10)%10+48);
}
#define IL inline
ll Q,pro2,lg[1000005];
namespace problem1{
const ll mod=1e9+7;
int n=read(),m=read(),a[500005],b[500005],ga[20][500005],gb[20][500005],maxa[20][500005],maxb[20][500005];
ll suma[500005],sumb[500005];
IL int g1(int l,int r){
if(l>r)return 0ll;
int xp=lg[r-l+1];
return __gcd(ga[xp][l],ga[xp][r-(1<<xp)+1]);
}
IL int g2(int l,int r){
if(l>r)return 0ll;
int xp=lg[r-l+1];
return __gcd(gb[xp][l],gb[xp][r-(1<<xp)+1]);
}
IL int m1(int l,int r){
if(l>r)return 0ll;
int xp=lg[r-l+1];
return max(maxa[xp][l],maxa[xp][r-(1<<xp)+1]);
}
IL int m2(int l,int r){
if(l>r)return 0ll;
int xp=lg[r-l+1];
return max(maxb[xp][l],maxb[xp][r-(1<<xp)+1]);
}
void main(){
Q=read();
for(int i=1;i<=n;i++)a[i]=read(),ga[0][i]=abs(a[i]-a[i-1]),maxa[0][i]=a[i],suma[i]=(suma[i-1]+a[i])%mod;
for(int j=1;j<=m;j++)b[j]=read(),gb[0][j]=abs(b[j]-b[j-1]),maxb[0][j]=b[j],sumb[j]=(sumb[j-1]+b[j])%mod;
for(int i=1;i<=19;i++)
for(int j=1;j<=n-(1<<i)+1;j++)
ga[i][j]=__gcd(ga[i-1][j],ga[i-1][j+(1<<i-1)]),maxa[i][j]=max(maxa[i-1][j],maxa[i-1][j+(1<<i-1)]);
for(int i=1;i<=19;i++)
for(int j=1;j<=m-(1<<i)+1;j++)
gb[i][j]=__gcd(gb[i-1][j],gb[i-1][j+(1<<i-1)]),maxb[i][j]=max(maxb[i-1][j],maxb[i-1][j+(1<<i-1)]);
for(int i=1;i<=Q;i++){
int oop=read(),h1=read(),lie1=read(),h2=read(),lie2=read();
if(oop==1)pro2=((suma[h2]-suma[h1-1]+mod)%mod*(lie2-lie1+1)%mod+(sumb[lie2]-sumb[lie1-1]+mod)%mod*(h2-h1+1)%mod)%mod;
if(oop==2)pro2=m1(h1,h2)+m2(lie1,lie2);
if(oop==3)pro2=__gcd(__gcd(g1(h1+1,h2),g2(lie1+1,lie2)),a[h1]+b[lie1]);
write(pro2),putchar(10);
}
return;
}
}
namespace problem2{
const int N=5e5+5;
int n,cnt,t,h[N],a[N],b[N],de[N],rt[N],fat[N],sum[N*22],lc[N*22],rc[N*22],num;
int dfn[N<<1],pos[N],tot,st[25][(N<<1)+2],rev[25][(N<<1)+2],depth[N<<1];
struct node{int y,nxt;}e[N<<1];
IL void add(int o1,int o2){e[cnt].nxt=h[o1],e[cnt].y=o2,h[o1]=cnt++;}
void build(int &o,int l,int r){
o=++num;
if(l==r)return;
int mid=l+r>>1;
build(lc[o],l,mid),build(rc[o],mid+1,r);
}
void Update(int &o,int pre,int l,int r,int x){
o=++num,lc[o]=lc[pre],rc[o]=rc[pre],sum[o]=sum[pre]+1;
if(l==r)return;
int mid=l+r>>1;
if(mid>=x)Update(lc[o],lc[pre],l,mid,x);
else Update(rc[o],rc[pre],mid+1,r,x);
}
int query1(int o1,int o2,int o3,int o4,int l,int r,int x){
if(l==r)return sum[o1]+sum[o2]-sum[o3]-sum[o4];
int mid=l+r>>1;
if(mid>=x)return query1(lc[o1],lc[o2],lc[o3],lc[o4],l,mid,x);
return query1(rc[o1],rc[o2],rc[o3],rc[o4],mid+1,r,x);
}
int query2(int o1,int o2,int o3,int o4,int l,int r,int L,int R){
if(l>=L&&r<=R)return sum[o1]+sum[o2]-sum[o3]-sum[o4];
int mid=l+r>>1,an=0;
if(mid>=L)an+=query2(lc[o1],lc[o2],lc[o3],lc[o4],l,mid,L,R);
if(mid<R)an+=query2(rc[o1],rc[o2],rc[o3],rc[o4],mid+1,r,L,R);
return an;
}
int query3(int o1,int o2,int o3,int o4,int l,int r){
if(l==r)return l;
int mid=l+r>>1;
if(sum[lc[o1]]+sum[lc[o2]]-sum[lc[o3]]-sum[lc[o4]]>0)return query3(lc[o1],lc[o2],lc[o3],lc[o4],l,mid);
return query3(rc[o1],rc[o2],rc[o3],rc[o4],mid+1,r);
}
void dfs(int x,int fa){
dfn[++tot]=x,pos[x]=tot,depth[tot]=de[x];
Update(rt[x],rt[fa],1,t,a[x]);
for(int i=h[x];i!=-1;i=e[i].nxt)
if(v!=fa)de[v]=de[x]+1,fat[v]=x,dfs(v,x),dfn[++tot]=x,depth[tot]=de[x];
}
void init() {
for(int i=1;i<=tot;i++)st[0][i]=depth[i],rev[0][i]=dfn[i];
for(int i=1;i<=lg[tot];i++)
for(int j=1;j+(1<<i)-1<=tot;j++){
if(st[i-1][j]<st[i-1][j+(1<<i-1)])st[i][j]=st[i-1][j],rev[i][j]=rev[i-1][j];
else st[i][j]=st[i-1][j+(1<<i-1)],rev[i][j]=rev[i-1][j+(1<<i-1)];
}
}
IL int lca(int l,int r){
int jf=min(pos[l],pos[r]);
r=max(pos[l],pos[r]),l=jf;
int k=lg[r-l+1];
return st[k][l]<st[k][r+1-(1<<k)]?rev[k][l]:rev[k][r+1-(1<<k)];
}
void main(){
n=Q;
memset(h,-1,sizeof(h));
for(int i=1;i<=n;i++)a[i]=b[i]=read();
sort(b+1,b+1+n),t=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+1+t,a[i])-b;
for(int i=1;i<n;i++){int o1=read(),o2=read();add(o1,o2),add(o2,o1);}
build(rt[0],1,t),dfs(1,0),init();
int lans=0;
for(int i=1;i<=Q;i++){
int op=read(),j=read(),k=read(),co,jf,LC;
j=(j+lans)%Q+1,k=(k+lans)%Q+1;
if(op==1){
co=read(),jf=lower_bound(b+1,b+1+t,co)-b;
if(b[jf]!=co||co==0){write(lans=0),putchar(10);continue;}
LC=lca(j,k),write(lans=query1(rt[j],rt[k],rt[LC],rt[fat[LC]],1,t,jf)),putchar(10);
}
if(op==2){
co=read(),jf=upper_bound(b+1,b+1+t,co)-b,jf--;
if(b[1]>co){write(lans=0),putchar(10);continue;}
LC=lca(j,k);
if(b[t]<=co){write(lans=de[j]+de[k]-de[LC]*2+1),putchar(10);continue;}
write(lans=query2(rt[j],rt[k],rt[LC],rt[fat[LC]],1,t,1,jf)),putchar(10);
}
if(op==3)LC=lca(j,k),write(lans=b[query3(rt[j],rt[k],rt[LC],rt[fat[LC]],1,t)]),putchar(10);
}
return;
}
}
int main(){
// freopen("1.in","r",stdin);
// freopen("1.out","w",stdout);
for(int i=2;i<=1e6;i++){lg[i]=((1<<(lg[i-1]+1))==i?lg[i-1]+1:lg[i-1]);}
problem1::main();
problem2::main();
return 0;
}
实际上,树链剖分+主席树也足以通过本题(毕竟树剖常数太小,加上出题人不想卡,也太菜了很难卡,就放过去了),但是码量会略大于纯主席树。