好像只有一个串串题可以做...
不会 dp 和数据结构啊 QAQ
10 + 20 + 100 = 130
T1
一棵树,每个点有一个能量的最大容量 $l_i$ 和一个增长速度 $v_i$,每次可以选一个点,给 q 个时刻,每次把这个子树里和它距离不超过 k 的点的能量全都拿走,求每次拿走了多少
$n,q leq 152501$
$Time space Limit : 4s$
sol:
暴力常数小即可 70,常数大(我)只有 20
被 yyc 爆踩辣
(为什么你会这么熟练啊!你到底写过多少暴力了 QAQ
subtask 1.没有最大容量
对于每个点,将 (dfs序,深度) 作为二元组搞到矩形里,每次拿走的是一个矩形,
用 kd 树维护这些点即可
每次期望影响 $sqrt{n}$ 个矩形和 $sqrt{n}$ 个单点,复杂度 $O(卡不掉)$
subtask 2.一条链
构造一个关于“最后一次采摘时间"的序列
进行一次操作最多让这个序列的段数改变 2
就比如说有可能 002200 -> 044200 -> 554200
对于每个区间开一个三元组 $(l,r,x)$ 表示区间 $[l,r]$ 最后一次采摘的时间是 $x$
每次相当于每次拆开两个三元组,对它们中间的计算答案,显然中间的整段可以一起计算
现在的问题就是对于 $(l,r,x)$ 和 $(l,r,t)$ ,能拿到多少能量
分类讨论, 如过 $lceil frac{l_i}{v_i} ceil leq t$,那就是 $sum l_i$
否则就是$t imes sum v_i$
用一个平衡树维护这些区间,然后用主席树查一遍区间有多少小于等于 $k$ 的元素就可以了
subtask 正解
还是 kd 树,搜出来一定是 $sqrt{n}$ 个子树和 $sqrt{n}$ 个单点,单点随便做一做
对于子树可以暴力递归下去找到“都被修改过”的子树
然后主席树合并,通过乱七八糟的复杂度分析大概是 $O(nlogn + q sqrt{n} logn)$
这也是 $Time space Limit : 4s$ 而且被暴力过了的原因
duliu
#include<cstdio> #include<cstring> #include<cctype> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) using namespace std; inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } #define L T[o].lc #define R T[o].rc typedef long long ll; const int maxn=160010; const int maxm=1000000000; const int maxnode=10000010; const int inf=1e9; int ls[maxnode],rs[maxnode],ToT; int n,m,c[maxn],lim[maxn],first[maxn],to[maxn],dis[maxn],next[maxn],e; int root[maxn],D; ll sumk[maxnode],sumv[maxnode],ans; struct Node { int mx[2],mn[2],x[2],lc,rc,v,sum; bool operator < (const Node& ths) const { return x[D]<ths.x[D]; } }T[maxn]; void maintain(int o) { T[o].sum=T[L].sum+T[R].sum+1; rep(c,0,1) { T[o].mx[c]=max(T[o].x[c],max(T[L].mx[c],T[R].mx[c])); T[o].mn[c]=min(T[o].x[c],min(T[L].mn[c],T[R].mn[c])); } } int setv[maxn],vali[maxn]; void pushdown(int o) { if(setv[o]>=0) { setv[L]=setv[R]=vali[L]=vali[R]=setv[o]; setv[o]=-1; } } int merge(int x,int y) { if(!x) return y; if(!y) return x; int z=++ToT; sumv[z]=sumv[x]+sumv[y];sumk[z]=sumk[x]+sumk[y]; ls[z]=merge(ls[x],ls[y]);rs[z]=merge(rs[x],rs[y]); return z; } void update(int& y,int x,int l,int r,int p,int v,int v2) { y=++ToT;sumk[y]=sumk[x]+v2;sumv[y]=sumv[x]+v; if(l==r) return; int mid=l+r>>1;ls[y]=ls[x];rs[y]=rs[x]; if(p<=mid) update(ls[y],ls[x],l,mid,p,v,v2); else update(rs[y],rs[x],mid+1,r,p,v,v2); } void build(int& o,int l,int r,int cur) { if(l>r) return;D=cur; int mid=l+r>>1;o=mid;nth_element(T+l,T+mid,T+r+1); build(L,l,mid-1,cur^1);build(R,mid+1,r,cur^1); root[o]=merge(root[L],root[R]);update(root[o],root[o],0,maxm,(lim[T[o].v]-1)/c[T[o].v],lim[T[o].v],c[T[o].v]); maintain(o); } ll sk,sv; void query(int o,int l,int r,int p) { if(!o) return; if(l==r) {sk+=sumk[o];sv+=sumv[o];} else { int mid=l+r>>1; if(p<=mid) query(ls[o],l,mid,p); else sk+=sumk[ls[o]],sv+=sumv[ls[o]],query(rs[o],mid+1,r,p); } } void calc(int o,int t) { if(!o) return; if(setv[o]>=0) { sk=sv=0;query(root[o],0,maxm,t-setv[o]-1); ans+=sv+(sumk[root[o]]-sk)*(t-setv[o]); setv[o]=-1; return; } else { int x=T[o].v; ans+=min((ll)lim[x],(ll)(t-vali[o])*c[x]); vali[o]=t; } calc(L,t);calc(R,t); } void query(int o,int x1,int x2,int y,int t) { if(!o) return; if(T[o].mn[1]>y||T[o].mx[0]<x1||T[o].mn[0]>x2) return; if(T[o].mx[1]<=y&&T[o].mn[0]>=x1&&T[o].mx[0]<=x2) { calc(o,t);setv[o]=vali[o]=t;return; } pushdown(o); if(T[o].x[1]<=y&&T[o].x[0]>=x1&&T[o].x[0]<=x2) { int x=T[o].v; ans+=min((ll)lim[x],(ll)(t-vali[o])*c[x]); vali[o]=t; } query(L,x1,x2,y,t);query(R,x1,x2,y,t); } void AddEdge(int u,int v,int w) { to[++e]=v;dis[e]=w;next[e]=first[u];first[u]=e; } int st[maxn],en[maxn],dep[maxn],cnt; void dfs(int x) { st[x]=++cnt; for(int i=first[x];i;i=next[i]) { dep[to[i]]=dep[x]+dis[i]; dfs(to[i]); } en[x]=cnt; } int main() { freopen("exploit.in","r",stdin); freopen("exploit.out","w",stdout); T[0].mx[0]=T[0].mx[1]=-inf; T[0].mn[1]=T[0].mn[0]=inf; n=read();int rt=0; rep(i,1,n) c[i]=read(); rep(i,1,n) lim[i]=read(); rep(i,2,n) { int f=read(),w=read(); AddEdge(f,i,w); } dfs(1); rep(i,1,n) T[i].x[0]=st[i],T[i].x[1]=dep[i],T[i].v=i; build(rt,1,n,0); m=read(); while(m--) { int t=read(),u=read(),k=read();ans=0; query(rt,st[u],en[u],k+dep[u],t); printf("%lld ",ans); } return 0; }
T2
给 $n$ 个点的坐标 $p_i$,每个点的参数 $a_i,b_i$和一个 $k$,第 $i$ 个点可以跳到标号为 $[i+1,i+k]$ 的点 $j$,代价为 $|p_i - p_j| imes b_i$,每到一个点,就会在这个点停留 $a_i$,求 $1$ 跳到 $n$ 的最小代价
$n leq 152501$ 坐标范围不超过 $152501$
sol:
subtask 1.k=n
分类讨论
当 $p_i ≥ p_j$ 时,要求 $f_j - p_j imes b_j + p_i imes b_j$
设一条直线 $y = (b_j) x + (f_j - p_j imes b_j)$,把 $p_i$ 带进去就是答案
用一个李超树维护直线就可以了
#include<bits/stdc++.h> #define LL long long #define ls (x << 1) #define rs ((x << 1) | 1) using namespace std; inline int read() { int x = 0,f = 1;char ch = getchar(); for(;!isdigit(ch);ch = getchar())if(ch == '-') f = -f; for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0'; return x * f; } const int maxn = 160010; const LL inf = (1LL << 60); int n,m,k; LL dp[maxn]; int p[maxn],b[maxn],a[maxn]; vector<int> items[maxn << 4]; int s[maxn << 8],top; struct Line{LL k,b;LL f(LL x){return k * x + b;}Line(){}Line(LL _k,LL _b){k = _k,b = _b;}}seg[maxn << 4]; inline void build(int x,int l,int r) { seg[x] = (Line){0,inf}; if(l == r)return; int mid = (l + r) >> 1; build(ls,l,mid);build(rs,mid + 1,r); } void rec(){while(top)seg[s[top--]] = (Line){0,inf};} inline void add_item(int x,int l,int r,int L,int R,int v) { if(L <= l && r <= R){items[x].push_back(v);return;} int mid = (l + r) >> 1; if(L <= mid)add_item(ls,l,mid,L,R,v); if(R > mid)add_item(rs,mid + 1,r,L,R,v); } inline void add_line(int x,int l,int r,Line cur) { s[++top] = x; if(l == r){if(cur.f(l) < seg[x].f(l))seg[x] = cur;return;} int mid = (l + r) >> 1; if(cur.f(mid) < seg[x].f(mid))swap(cur,seg[x]); if(cur.f(l) < seg[x].f(l))add_line(ls,l,mid,cur); if(cur.f(r) < seg[x].f(r))add_line(rs,mid + 1,r,cur); } void get_pos(int x,int l,int r,int L,int R,Line cur) { if(L <= l && r <= R){add_line(x,l,r,cur);return;} int mid = (l + r) >> 1; if(L <= mid)get_pos(ls,l,mid,L,R,cur); if(R > mid)get_pos(rs,mid + 1,r,L,R,cur); } LL query(int x,int l,int r,int pos) { if(l == r)return seg[x].f(pos); int mid = (l + r) >> 1; if(pos <= mid)return min(seg[x].f(pos),query(ls,l,mid,pos)); else return min(seg[x].f(pos),query(rs,mid + 1,r,pos)); } void solve(int x,int l,int r) { int sz = items[x].size(); for(int i=0;i<sz;i++) { int targ = items[x][i]; get_pos(1,1,m,1,p[targ],Line(-b[targ],dp[targ] + (LL)p[targ] * b[targ])); get_pos(1,1,m,p[targ],m,Line(b[targ],dp[targ] - (LL)p[targ] * b[targ])); } if(sz != 0) { for(int i=l;i<=r;i++)dp[i] = min(dp[i],query(1,1,m,p[i]) + a[i]); rec(); } int mid = (l + r) >> 1; if(l == r)return; solve(ls,l,mid);solve(rs,mid + 1,r); } signed main() { freopen("cruise.in","r",stdin); freopen("cruise.out","w",stdout); n = read(),m = read(),k = read(); for(int i=1;i<=n;i++)p[i] = read(),dp[i] = inf; for(int i=1;i<=n;i++)a[i] = read(); for(int i=1;i<=n;i++)b[i] = read(); dp[1] = a[1]; for(int i=1;i<n;i++)add_item(1,1,n,i + 1,min(i + k,n),i); build(1,1,m);solve(1,1,n); cout<<dp[n]<<endl; }
subtask 正解
王 · 线段树分治 · 子健
线段树分治,然后就变成了上一个 subtask,撤销跟并查集一样,用一个栈维护所有操作, 撤销那几个操作的影响即可
$O(nlog^3n)$ 看似不怎么可过,但李超树的两个 log 跟一个 log 差不多,所以可以过
T3
定义 $f(S)$ 为字符串 $S$ 的 kmp 树上每个点的深度和(根的深度为 $1$),给 $q$ 个询问,每组一个 $m$ ,求 $sum_{1 leq l leq r leq m} f(S[l cdots r])$
$|S| leq 152501,q leq 152501$
sol:
要求一个前缀和,差分一下,就是要求一个串后加一个新字符,kmp 树的深度和
于是再差分一下,变成了一个点在 kmp 树上的深度,这个能 fail 多少次就是多少,相当于它在这个前缀里出现的次数
然后就相当于一个字符串,每次添加一个字符,询问一个后缀出现了多少次
那就是后缀自动机板题,结尾相同的所有子串是一条 parent 树上的链,维护一下这条每个点有多少不同的串即可
然后就是一个链加 & 链查询
树链剖分 / LCT / 全局平衡二叉树即可
#include<bits/stdc++.h> #define LL long long using namespace std; inline int read() { int x = 0,f = 1;char ch = getchar(); for(;!isdigit(ch);ch = getchar())if(ch == '-') f = -f; for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0'; return x * f; } const int maxn = 400010,mod = 998244353; char s[maxn]; int n,q; int mxlen[maxn],fa[maxn],tr[maxn][26]; int root,last,dfn; inline void extend(int c) { int p = last,np = last = ++dfn; mxlen[np] = mxlen[p] + 1; while(p && !tr[p][c])tr[p][c] = np,p = fa[p]; if(!p)fa[np] = root; else { int q = tr[p][c]; if(mxlen[q] == mxlen[p] + 1)fa[np] = q; else { int nq = ++dfn; mxlen[nq] = mxlen[p] + 1;memcpy(tr[nq],tr[q],sizeof(tr[nq]));fa[nq] = fa[q],fa[np] = fa[q] = nq; while(p && tr[p][c] == q)tr[p][c] = nq,p = fa[p]; } } } int first[maxn],to[maxn << 1],nx[maxn << 1],cnt; inline void add(int u,int v){to[++cnt] = v;nx[cnt] = first[u];first[u] = cnt;} int size[maxn],dep[maxn],bl[maxn],pos[maxn],reh[maxn],_tms; int anc[maxn]; inline void dfs1(int x) { size[x] = 1; for(int i=first[x];i;i=nx[i]) { if(to[i] == anc[x])continue; anc[to[i]] = x;dep[to[i]] = dep[x] + 1; dfs1(to[i]);size[x] += size[to[i]]; } } inline void dfs2(int x,int col) { int k = 0; bl[x] = col; pos[x] = ++_tms; reh[_tms] = x; for(int i=first[x];i;i=nx[i]) if(dep[to[i]] > dep[x] && size[to[i]] > size[k])k = to[i]; if(!k)return; dfs2(k,col); for(int i=first[x];i;i=nx[i]) if(dep[to[i]] > dep[x] && to[i] != k)dfs2(to[i],to[i]); } #define ls (x << 1) #define rs ((x << 1) | 1) int seg[maxn << 2],tag[maxn << 2],sum[maxn << 2]; inline void pushup(int x){sum[x] = (sum[ls] + sum[rs]) % mod;} inline void pushdown(int x) { if(!tag[x])return; sum[ls] = (sum[ls] + tag[x] * seg[ls]) % mod; sum[rs] = (sum[rs] + tag[x] * seg[rs]) % mod; tag[ls] = (tag[ls] + tag[x]) % mod; tag[rs] = (tag[rs] + tag[x]) % mod; tag[x] = 0; } inline void build(int x,int l,int r) { if(l == r) { seg[x] = mxlen[reh[l]] - mxlen[fa[reh[l]]]; return; } int mid = (l + r) >> 1; build(ls,l,mid);build(rs,mid + 1,r); seg[x] = (seg[ls] + seg[rs]) % mod; } inline void update(int x,int l,int r,int L,int R) { if(L <= l && r <= R) { tag[x]++; sum[x] = (sum[x] + seg[x]) % mod; return; } pushdown(x); int mid = (l + r) >> 1; if(L <= mid)update(ls,l,mid,L,R); if(R > mid)update(rs,mid + 1,r,L,R); pushup(x); } inline int query(int x,int l,int r,int L,int R) { if(L <= l && r <= R)return sum[x]; pushdown(x); int mid = (l + r) >> 1,ans = 0; if(L <= mid)ans = (ans + query(ls,l,mid,L,R)) % mod; if(R > mid)ans = (ans + query(rs,mid + 1,r,L,R)) % mod; return ans; } inline void modify(int x) { while(bl[x] != bl[1]) { update(1,1,dfn,pos[bl[x]],pos[x]); x = anc[bl[x]]; }update(1,1,dfn,pos[1],pos[x]); } inline int ask(int x) { int ans = 0; while(bl[x] != bl[1]) { ans = (ans + query(1,1,dfn,pos[bl[x]],pos[x])) % mod; x = anc[bl[x]]; } ans = (ans + query(1,1,dfn,pos[1],pos[x])) % mod; return ans; } int hh[maxn]; int ret[maxn]; int main() { freopen("zoo.in","r",stdin); freopen("zoo.out","w",stdout); root = last = ++dfn; scanf("%s",s + 1); n = strlen(s + 1); for(int i=1;i<=n;i++)extend(s[i] - 'a'); for(int i=2;i<=dfn;i++)add(fa[i],i); dfs1(root);dfs2(root,root); build(1,1,dfn); int q = read(); int ans = 0,now = 1,res = 0; for(int i=1;i<=n;i++) { now = tr[now][s[i] - 'a']; res = (res + ask(now) + i) % mod; ans = (ans + res) % mod; modify(now); ret[i] = ans; } for(int i=1;i<=q;i++) { int x = read(); printf("%d ",ret[x]); } }