zoukankan      html  css  js  c++  java
  • NOI模拟赛 #4

    好像只有一个串串题可以做...

    不会 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;
    }
    T1

    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;
    }
    T2

    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]);
        }
    }
    T3
  • 相关阅读:
    PAT (Advanced Level) 1086. Tree Traversals Again (25)
    PAT (Advanced Level) 1085. Perfect Sequence (25)
    PAT (Advanced Level) 1084. Broken Keyboard (20)
    PAT (Advanced Level) 1083. List Grades (25)
    PAT (Advanced Level) 1082. Read Number in Chinese (25)
    HDU 4513 吉哥系列故事――完美队形II
    POJ Oulipo KMP 模板题
    POJ 3376 Finding Palindromes
    扩展KMP
    HDU 2289 Cup
  • 原文地址:https://www.cnblogs.com/Kong-Ruo/p/10133317.html
Copyright © 2011-2022 走看看