12.17 模拟赛
T1 exploit
题目大意:
一棵树 每个点中有能量井,每个时刻,第$i$口井中回复$v_i$的能量;每口井有能量上限$l_i$
$Q$次询问 每次询问$t,x,k$表示在$t$时刻提取$x$的子树中与$x$距离不超过$k$的井的能量 并输出提取的能量之和 保证$t$递增
思路:
首先题目被分为了两个关键部分:找到子树中距离不超过$k$的点与解决能量上限的问题
对于第一个子问题 可以将树上的第$i$个点hash成平面中坐标为$(in_i,dep_i)$的点
于是可以建立$kd-tree$ 查询相当于查询一个边界为$x=in_i,x=out_i,y=0,y=dep_i+k$矩形中的点 kd树基操
修改的时候需要知道整个子树上一次修改的时间是否相同 如果不同就继续递归 否则整体修改
(复杂度势能分析 会是不可能会的
对于第二个子问题 我们在$kd-tree$的每个节点都设前缀和数组来方便统计子树的统计
$limsum_i$表示该点子树中$\frac {lim_j}{v_j} \le i$的点的$lim$和 $vsum_i$表示该点子树中$\frac {lim_j}{v_j} \le i$的$v$之和
对于一整个子树 设$x=t-$上次修改的时间,则答案为$limsum_x + (vsum_max- vsum_x) \times x$即可以表示已经到达上界和未到达上界的两部分
而快速构造这个数组则可以在$build \space kd-tree$时线段树合并
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cmath> 5 #include<algorithm> 6 #include<cstring> 7 #include<vector> 8 #include<stack> 9 #include<queue> 10 #include<map> 11 #define rep(i,s,t) for(register int i=(s),i__end=(t);i<=i__end;++i) 12 #define dwn(i,s,t) for(register int i=(s),i__end=(t);i>=i__end;--i) 13 #define ren for(int i=fst[x];i;i=nxt[i]) 14 #define Fill(x,t) memset(x,t,sizeof(x)) 15 #define ll long long 16 #define ull unsigned long long 17 #define inf 1000000000 18 #define MAXN 170100 19 #define MOD 998244353 20 using namespace std; 21 inline int read() 22 { 23 int x=0,f=1;char ch=getchar(); 24 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 25 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 26 return x*f; 27 } 28 int n,m,dep[MAXN],fa[MAXN],fst[MAXN],to[MAXN],nxt[MAXN],val[MAXN],ls[MAXN<<6],rs[MAXN<<6],rt[MAXN]; 29 int inc[MAXN],mx[MAXN],in[MAXN],ou[MAXN],cnt,Dim,tag[MAXN],tim[MAXN],las[MAXN],tot; 30 ll smx[MAXN<<6],sinc[MAXN<<6],ans,s1,s2; 31 void add(int u,int v,int w) {nxt[++cnt]=fst[u],fst[u]=cnt,to[cnt]=v,val[cnt]=w;} 32 void dfs(int x){in[x]=++cnt;ren dep[to[i]]=dep[x]+val[i],dfs(to[i]);ou[x]=cnt;} 33 struct node {int mx[2],mn[2],d[2],id,l,r,sz;node(){l=r=sz=0;}}; 34 bool operator < (const node a,const node b) {return a.d[Dim]<b.d[Dim];} 35 void mdf(int &k,int kk,int l,int r,int p,int x1,int x2) 36 { 37 k=++tot;smx[k]=smx[kk]+x1,sinc[k]=sinc[kk]+x2; 38 if(l==r) return ;int mid=l+r>>1;ls[k]=ls[kk],rs[k]=rs[kk]; 39 if(p<=mid) mdf(ls[k],ls[kk],l,mid,p,x1,x2);else mdf(rs[k],rs[kk],mid+1,r,p,x1,x2); 40 } 41 int merge(int x,int y) 42 { 43 if(!(x*y)) return x|y;int z=++tot;smx[z]=smx[x]+smx[y]; 44 sinc[z]=sinc[x]+sinc[y],ls[z]=merge(ls[x],ls[y]),rs[z]=merge(rs[x],rs[y]);return z; 45 } 46 void Query(int k,int l,int r,int x) 47 { 48 if(!k) return ;if(l==r) return ; 49 int mid=l+r>>1;if(x<=mid) Query(ls[k],l,mid,x); 50 else {s1+=smx[ls[k]],s2+=sinc[ls[k]];Query(rs[k],mid+1,r,x);} 51 } 52 struct kd_tree 53 { 54 node tr[MAXN],l,r;int Rt; 55 void upd(int k) 56 { 57 l=tr[tr[k].l],r=tr[tr[k].r],tr[k].sz=l.sz+r.sz+1; 58 rep(i,0,1) 59 { 60 if(tr[k].l) tr[k].mx[i]=max(tr[k].mx[i],l.mx[i]),tr[k].mn[i]=min(tr[k].mn[i],l.mn[i]); 61 if(tr[k].r) tr[k].mx[i]=max(tr[k].mx[i],r.mx[i]),tr[k].mn[i]=min(tr[k].mn[i],r.mn[i]); 62 } 63 } 64 int build(int l,int r,int now) 65 { 66 int mid=l+r>>1;Dim=now;nth_element(tr+l,tr+mid,tr+r+1);int id=tr[mid].id; 67 rep(i,0,1) tr[mid].mn[i]=tr[mid].mx[i]=tr[mid].d[i]; 68 if(l<mid) tr[mid].l=build(l,mid-1,now^1); 69 if(mid<r) tr[mid].r=build(mid+1,r,now^1);upd(mid); 70 rt[mid]=merge(rt[tr[mid].l],rt[tr[mid].r]); 71 mdf(rt[mid],rt[mid],0,inf,tim[id],mx[id],inc[id]);return mid; 72 } 73 void mem(){rep(i,1,n) tr[i].id=i,tr[i].d[0]=in[i],tr[i].d[1]=dep[i];Rt=build(1,n,0);} 74 void pshd(int k){tag[tr[k].l]=tag[tr[k].r]=las[tr[k].l]=las[tr[k].r]=tag[k],tag[k]=-1;} 75 void work(int k,int t) 76 { 77 if(!k) return ; 78 if(tag[k]>=0) {s1=s2=0LL;Query(rt[k],0,inf,t-tag[k]);ans+=s1+(sinc[rt[k]]-s2)*((ll)t-tag[k]);return ;} 79 else ans+=min((ll)mx[tr[k].id],(ll)inc[tr[k].id]*(t-las[k])),las[k]=t; 80 work(tr[k].l,t);work(tr[k].r,t); 81 } 82 void query(int k,int x1,int x2,int y,int t) 83 { 84 if(!k) return ;if(tr[k].mx[0]<x1||tr[k].mn[0]>x2||tr[k].mn[1]>y) return ; 85 if(tr[k].mn[0]>=x1&&tr[k].mx[0]<=x2&&tr[k].mx[1]<=y){work(k,t);tag[k]=las[k]=t;return ;} 86 if(tag[k]>=0) pshd(k);if(tr[k].d[0]>=x1&&tr[k].d[0]<=x2&&tr[k].d[1]<=y) 87 ans+=min((ll)mx[tr[k].id],(ll)inc[tr[k].id]*(t-las[k])),las[k]=t; 88 query(tr[k].l,x1,x2,y,t);query(tr[k].r,x1,x2,y,t); 89 } 90 }kd; 91 int main() 92 { 93 freopen("exploit.in","r",stdin); 94 freopen("exploit.out","w",stdout); 95 n=read();int Q,t,x,k;rep(i,1,n) inc[i]=read();rep(i,1,n) mx[i]=read(),tim[i]=(mx[i]-1)/inc[i]; 96 rep(i,2,n) fa[i]=read(),add(fa[i],i,read());cnt=0;dfs(1);Q=read();kd.mem(); 97 while(Q--) {t=read(),x=read(),k=read(),ans=0LL;kd.query(kd.Rt,in[x],ou[x],dep[x]+k,t);printf("%lld\n",ans);} 98 }
T2 cruise
题目大意:
$n$个城市 每个城市的坐标为$p_i$,到达每个城市必须花费$a_i$的时间来游玩
从编号为$i$的城市可以到达编号为$[i+1,i+k]$的城市 路程花费时间为$|p_i-p_j| \times b_i$
求从第一个城市到第$n$个城市的最短时间
思路:
可以得到dp方程: $dp_i = dp_j + |p_i-p_j| \times b_i ,i-k \le j \le i-1$
一眼斜优+线段树分治 但是发现这个dp方程要分类讨论很难搞
这些直线可以用李超树维护(学到了斜优的新姿势
依然线段树分治 对于每个点 将该点对应的直线分为两部分 分别加入到李超线段树中
对于$[1,p_i]$ 加入$k=-b_i,b=dp_i+p_j \times b_i$的直线;
对于$[p_i,m]$ 加入$k=b_i,b=dp_i-p_j \times b_i$的直线
因为李超线段树无法撤销 所以对于每个线段树分治中线段树的节点 都需要在当前节点统计对下面叶子节点的影响并消除这些线段
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cmath> 5 #include<algorithm> 6 #include<cstring> 7 #include<vector> 8 #include<stack> 9 #include<queue> 10 #include<map> 11 #define rep(i,s,t) for(register int i=(s),i__end=(t);i<=i__end;++i) 12 #define dwn(i,s,t) for(register int i=(s),i__end=(t);i>=i__end;--i) 13 #define ren(x) for(int i=fst[x];i;i=nxt[i]) 14 #define Fill(x,t) memset(x,t,sizeof(x)) 15 #define ll long long 16 #define ull unsigned long long 17 #define inf 1LL<<60 18 #define MAXN 170100 19 #define MOD 998244353 20 using namespace std; 21 inline int read() 22 { 23 int x=0,f=1;char ch=getchar(); 24 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 25 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 26 return x*f; 27 } 28 int n,m,k,p[MAXN],a[MAXN],b[MAXN],s[MAXN<<5],top; 29 int fst[MAXN<<2],nxt[MAXN<<5],to[MAXN<<5],cnt; 30 ll dp[MAXN]; 31 void add(int u,int v) {nxt[++cnt]=fst[u],fst[u]=cnt,to[cnt]=v;} 32 struct seg{ll k,b;ll f(int x){return k*x+b;}}val[MAXN<<2]; 33 void build(int k,int l,int r) 34 { 35 val[k]=(seg){0,inf};if(l==r) return ;int mid=l+r>>1; 36 build(k<<1,l,mid);build(k<<1|1,mid+1,r); 37 } 38 void mdf(int k,int l,int r,seg x) 39 { 40 s[++top]=k; 41 if(l==r) {if(x.f(l)<val[k].f(l)) val[k]=x;return ;} 42 int mid=l+r>>1; 43 if(x.f(mid)<val[k].f(mid)) swap(val[k],x); 44 if(x.f(l)<val[k].f(l)) mdf(k<<1,l,mid,x);if(x.f(r)<val[k].f(r)) mdf(k<<1|1,mid+1,r,x); 45 } 46 void del(){while(top) val[s[top--]]=(seg){0,inf};} 47 void ins(int k,int l,int r,int a,int b,seg x) 48 { 49 if(l==a&&r==b) {mdf(k,a,b,x);return ;} 50 int mid=l+r>>1; 51 if(b<=mid) ins(k<<1,l,mid,a,b,x); 52 else if(a>mid) ins(k<<1|1,mid+1,r,a,b,x); 53 else {ins(k<<1,l,mid,a,mid,x);ins(k<<1|1,mid+1,r,mid+1,b,x);} 54 } 55 ll query(int k,int l,int r,int x) 56 { 57 if(l==r) return val[k].f(x); 58 int mid=l+r>>1;return min(val[k].f(x),x<=mid?query(k<<1,l,mid,x):query(k<<1|1,mid+1,r,x)); 59 } 60 void Mdf(int k,int l,int r,int a,int b,int x) 61 { 62 if(l==a&&r==b) {add(k,x);return ;} 63 int mid=l+r>>1; 64 if(b<=mid) Mdf(k<<1,l,mid,a,b,x); 65 else if(a>mid) Mdf(k<<1|1,mid+1,r,a,b,x); 66 else {Mdf(k<<1,l,mid,a,mid,x);Mdf(k<<1|1,mid+1,r,mid+1,b,x);} 67 } 68 void solve(int k,int l,int r) 69 { 70 ren(k) ins(1,1,m,1,p[to[i]],(seg){-b[to[i]],dp[to[i]]+(ll)p[to[i]]*b[to[i]]}), 71 ins(1,1,m,p[to[i]],m,(seg){b[to[i]],dp[to[i]]-(ll)p[to[i]]*b[to[i]]}); 72 if(fst[k]) {rep(i,l,r) dp[i]=min(dp[i],query(1,1,m,p[i])+a[i]);del();} 73 if(l==r) return ;int mid=l+r>>1;solve(k<<1,l,mid);solve(k<<1|1,mid+1,r); 74 } 75 int main() 76 { 77 freopen("cruise.in","r",stdin); 78 freopen("cruise.out","w",stdout); 79 n=read(),m=read(),k=read();rep(i,1,n) p[i]=read();rep(i,1,n) a[i]=read(),dp[i]=inf;rep(i,1,n) b[i]=read(); 80 dp[1]=a[1];rep(i,1,n-1) Mdf(1,1,n,i+1,min(i+k,n),i);build(1,1,m);solve(1,1,n);printf("%lld\n",dp[n]); 81 }
T3 zoo
题目大意:
定义$f(S)$为对一个串$S$建立KMP树,其中0节点深度为0,树上所有节点的深度和
给一个字符串$S$,$Q$次询问 每次询问一个$x$ 求$\sum_{l=1}^x \sum_{r=l}^x f(S[l:r])$
思路:
预处理出串中每个位置的答案 考虑$x+1$的答案与$x$的答案相差多少即所有$\sum_{l=1}^{x+1} f(S[l:x+1])$
发现这些KMP树实际上与之前的树并无什么区别 只是增加了这个节点 所以可以再次差分
因此考虑如何计算这个点的在各个树中的深度之和 发现每棵树的深度即为跳过border的次数 +1
计算跳过border的次数之和等价于计算以这个点结尾的字符串的出现次数之和
所有串出现次数之和为不同串的个数$\times$出现次数
每个串的出现次数即$end-pos$集合大小为它子树的大小
每新进一个点 我们可以将该点到根的路径上的每个点+该点包含不同串的个数,答案+不同串个数$\times$ $end-pos$集合大小
可以选择用LCT或离线+树链剖分来解决上述问题
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cmath> 5 #include<algorithm> 6 #include<cstring> 7 #include<vector> 8 #include<queue> 9 #include<map> 10 #define rep(i,s,t) for(register int i=(s),i__end=(t);i<=i__end;++i) 11 #define dwn(i,s,t) for(register int i=(s),i__end=(t);i>=i__end;--i) 12 #define ren for(int i=fst[x];i;i=nxt[i]) 13 #define Fill(x,t) memset(x,t,sizeof(x)) 14 #define ll long long 15 #define inf 2139062143 16 #define MAXN 350100 17 #define MOD 998244353 18 using namespace std; 19 inline int read() 20 { 21 int x=0,f=1;char ch=getchar(); 22 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 23 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 24 return x*f; 25 } 26 int n,fa[MAXN],tr[MAXN][26],mxl[MAXN],tot,lst,rt,sz[MAXN],cnt,ans[MAXN]; 27 int fst[MAXN],nxt[MAXN<<1],to[MAXN<<1],hsh[MAXN],HSH[MAXN],bl[MAXN],dep[MAXN]; 28 char ch[MAXN]; 29 inline void extend(int c) 30 { 31 int p=lst,np=lst=++tot;mxl[np]=mxl[p]+1,sz[np]=1; 32 for(;p&&!tr[p][c];p=fa[p]) tr[p][c]=np; 33 if(!p) {fa[np]=rt;return ;} 34 int q=tr[p][c];if(mxl[q]==mxl[p]+1) {fa[np]=q;return ;} 35 int nq=++tot;mxl[nq]=mxl[p]+1; 36 memcpy(tr[nq],tr[q],sizeof(tr[nq]));fa[nq]=fa[q],fa[np]=fa[q]=nq; 37 for(;p&&tr[p][c]==q;p=fa[p]) tr[p][c]=nq; 38 } 39 void add(int u,int v) {nxt[++cnt]=fst[u],fst[u]=cnt,to[cnt]=v;} 40 void dfs(int x) 41 { 42 ren {dep[to[i]]=dep[x]+1;dfs(to[i]);sz[x]+=sz[to[i]];}sz[x]++; 43 } 44 void dfs(int x,int anc) 45 { 46 hsh[x]=++cnt,HSH[cnt]=x,bl[x]=anc;int hvs=0; 47 ren if(sz[to[i]]>sz[hvs]) hvs=to[i]; 48 if(!hvs) return ;dfs(hvs,anc); 49 ren if(to[i]!=hvs) dfs(to[i],to[i]); 50 } 51 int sum[MAXN<<2],tag[MAXN<<2],v[MAXN<<2],num[MAXN]; 52 void pshd(int k) 53 { 54 (sum[k<<1]+=(ll)tag[k]*v[k<<1])%=MOD,(sum[k<<1|1]+=(ll)tag[k]*v[k<<1|1]); 55 tag[k<<1]+=tag[k],tag[k<<1|1]+=tag[k],tag[k]=0; 56 } 57 void build(int k,int l,int r) 58 { 59 if(l==r) {v[k]=mxl[HSH[l]]-mxl[fa[HSH[l]]];return ;} 60 int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r); 61 v[k]=(v[k<<1]+v[k<<1|1])%MOD; 62 } 63 void mdf(int k,int l,int r,int a,int b) 64 { 65 if(l==a&&r==b) {(sum[k]+=v[k])%=MOD,tag[k]++;return ;} 66 int mid=l+r>>1;if(tag[k]) pshd(k); 67 if(b<=mid) mdf(k<<1,l,mid,a,b); 68 else if(a>mid) mdf(k<<1|1,mid+1,r,a,b); 69 else {mdf(k<<1,l,mid,a,mid);mdf(k<<1|1,mid+1,r,mid+1,b);} 70 sum[k]=(sum[k<<1]+sum[k<<1|1])%MOD; 71 } 72 int query(int k,int l,int r,int a,int b) 73 { 74 if(l==a&&r==b) return sum[k]; 75 int mid=l+r>>1;if(tag[k]) pshd(k); 76 if(b<=mid) return query(k<<1,l,mid,a,b); 77 else if(a>mid) return query(k<<1|1,mid+1,r,a,b); 78 else return (query(k<<1,l,mid,a,mid)+query(k<<1|1,mid+1,r,mid+1,b))%MOD; 79 } 80 int Query(int x,int res=0) 81 { 82 while(bl[x]!=1) (res+=query(1,1,tot,hsh[bl[x]],hsh[x]))%=MOD,x=fa[bl[x]]; 83 return res+query(1,1,tot,1,hsh[x]); 84 } 85 void Mdf(int x) 86 { 87 while(bl[x]!=1) mdf(1,1,tot,hsh[bl[x]],hsh[x]),x=fa[bl[x]]; 88 mdf(1,1,tot,1,hsh[x]); 89 } 90 int main() 91 { 92 freopen("zoo.in","r",stdin); 93 freopen("zoo.out","w",stdout); 94 scanf("%s",ch+1);lst=tot=rt=1;n=strlen(ch+1);rep(i,1,n) extend(ch[i]-'a'); 95 rep(i,1,tot) add(fa[i],i);cnt=0;dfs(1);dfs(1,1);build(1,1,tot);int pos=1; 96 rep(i,1,n) ans[i]=Query(pos=tr[pos][ch[i]-'a'])+i,Mdf(pos); 97 rep(i,1,n) (ans[i]+=ans[i-1])%=MOD;rep(i,1,n) (ans[i]+=ans[i-1])%=MOD;int Q=read(); 98 while(Q--) printf("%d\n",ans[read()]); 99 }