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) imes 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
",ans);}
98 }
View Code

T2 cruise

题目大意:

$n$个城市 每个城市的坐标为$p_i$,到达每个城市必须花费$a_i$的时间来游玩

从编号为$i$的城市可以到达编号为$[i+1,i+k]$的城市 路程花费时间为$|p_i-p_j| imes b_i$

求从第一个城市到第$n$个城市的最短时间

思路:

可以得到dp方程: $dp_i = dp_j + |p_i-p_j| imes b_i ,i-k le j le i-1$

一眼斜优+线段树分治 但是发现这个dp方程要分类讨论很难搞

这些直线可以用李超树维护(学到了斜优的新姿势

依然线段树分治 对于每个点 将该点对应的直线分为两部分 分别加入到李超线段树中

对于$[1,p_i]$ 加入$k=-b_i,b=dp_i+p_j imes b_i$的直线;

对于$[p_i,m]$ 加入$k=b_i,b=dp_i-p_j imes 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
",dp[n]);
81 }
View Code

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的次数之和等价于计算以这个点结尾的字符串的出现次数之和

所有串出现次数之和为不同串的个数$ imes$出现次数

每个串的出现次数即$end-pos$集合大小为它子树的大小

每新进一个点 我们可以将该点到根的路径上的每个点+该点包含不同串的个数,答案+不同串个数$ imes$ $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
",ans[read()]);
99 }
View Code
原文地址:https://www.cnblogs.com/yyc-jack-0920/p/10136423.html