zoukankan      html  css  js  c++  java
  • [冬令营模拟]wzj的题目#1

    T1 少膜一个,T3 暴力写挂

    强势 rank1 -> rank2

    一场比赛两道线段树分治,给力

    T1 password

    给你 m 个禁止字符串,求长度为 n 的所有字符串中至少包含这些禁止字符串各一次的字符串数量

    $n leq 10^9,m leq 4,sum len leq 50$

    sol:容斥一下就变成了“m 个禁止字符串,一个都不出现的字符串数量”

    这个可以转化成从 init 节点走 x 步走不到任意一个节点的方案数

    矩阵加速即可

    需要注意的是状态压缩被卡了,这种状压 + 矩乘可以用容斥来把矩阵变小

    比赛的时候因为少膜了一下,挂了一个点

    #include<bits/stdc++.h>
    #define int long long
    #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 N = 150;
    const int A = 10;
    const int MOD = 998244353;
    
    typedef vector<int> vec;
    typedef vector<vec> mat;
    
    mat mul(mat &A, mat &B)
    {
        mat C(A.size(), vec(B[0].size()));
        for (int i = 0; i < A.size(); ++i)
        {
            for (int k = 0; k < B.size(); ++k)
            {
                for (int j = 0; j < B[0].size(); ++j)
                {
                    C[i][j] = (C[i][j] + (LL)A[i][k] * B[k][j]) % MOD;
                }
            }
        }
        return C;
    }
    
    mat pow(mat A, int n)
    {
        mat B(A.size(), vec(A.size()));
        for (int i = 0; i < A.size(); ++i)
            B[i][i] = 1;
        while (n > 0)
        {
            if (n & 1) B = mul(B, A);
            A = mul(A, A);
            n >>= 1;
        }
        return B;
    }
    
    struct ACAutomata
    {
    
        int next[N][A], fail[N], end[N];
        int root, L;
    
        int idx(char ch)
        {
            return ch - '0';
        }
        int newNode()
        {
            for (int i = 0; i < A; ++i) next[L][i] = -1;
            end[L] = 0;
            return L++;
        }
        void init()
        {
            L = 0;
            root = newNode();
        }
        void insert(char buf[])
        {
            int len = strlen(buf);
            int now = root;
            for (int i = 0; i < len; ++i)
            {
                int ch = idx(buf[i]);
                if (next[now][ch] == -1) next[now][ch] = newNode();
                now = next[now][ch];
            }
            end[now]++;
        }
        void build()
        {
            queue<int> Q;
            fail[root] = root;
            for (int i = 0; i < A; ++i)
            {
                if (next[root][i] == -1)
                {
                    next[root][i] = root;
                }
                else
                {
                    fail[ next[root][i] ] = root;
                    Q.push( next[root][i] );
                }
            }
            while (!Q.empty())
            {
                int now = Q.front();
                Q.pop();
                if (end[ fail[now] ]) end[now]++;
                for (int i = 0; i < A; ++i) 
                {
                    if (next[now][i] == -1)
                    {
                        next[now][i] = next[ fail[now] ][i];
                    }
                    else
                    {
                        fail[ next[now][i] ] = next[ fail[now] ][i];
                        Q.push(next[now][i]);
                    }
                }
            }
        }
    
        int query(int n)
        {
            mat F(L, vec(L));
            for (int i = 0; i < L; ++i)
            {
                for (int j = 0; j < L; ++j)
                {
                    F[i][j] = 0;
                }
            }
            for (int i = 0; i < L; ++i)
            {
                for (int j = 0; j < A; ++j)
                {
                    int nt = next[i][j];
                    if (!end[nt]) F[i][nt]++;
                }
            }
            F = pow(F, n);
            int res = 0;
            for (int i = 0; i < L; ++i)
            {
                res = (res + F[0][i]) % MOD;
            }
            return res;
        }
    
    } ac;
    int m,n;
    char buf[20][60];
    int skr(int x,int t)
    {
        int res = 1;
        while(t)
        {
            if(t & 1)res = res * x % MOD;
            x = x * x % MOD;
            t = t >> 1;
        }return res;
    }
    signed main()
    {
        freopen("password.in","r",stdin);
        freopen("password.out","w",stdout);
        m = read(),n = read();
        int qwq = 0,qnq = skr(10,n);
        for(int i=1;i<=m;i++)cin>>buf[i];
        for(int SS=1;SS<=(1<<m)-1;SS++)
        {
            int flg = __builtin_popcount(SS),S = (flg & 1) ? 1 : -1;
            ac.init();
            memset(ac.next,-1,sizeof(ac.next));
            memset(ac.fail,0,sizeof(ac.fail));
            memset(ac.end,0,sizeof(ac.end));
            for(int i=0;i<m;i++)
                if(SS & (1 << i))ac.insert(buf[i+1]);
            ac.build();
            qwq += (S * ac.query(n));
        }
        cout<<((((qnq-qwq) % MOD) + MOD) % MOD)<<endl;
        return 0;
    }
    T1

    T2 paint

    APIO2014 特别行动队,但是所有东西都可以为负

    额...就是你现在有 n 个人,每个人有权值,你可以把它们分成若干组,第 i 人只能跟 $[i-l,i+r]$ 这些人一组,一组的权值和为 $s$,一组的权值为 $ax^2+bx+c$ ,让你最大化权值

    sol:因为一个人只能由 $[i-l,i+r]$ 转移过来,我们可以考虑这个人有贡献的区间,那显然是 $[i+l,min(i+r,n)]$ ,我们可以在线段树这个区间上打上这个人的 tag

    然后我们按线段树的 dfs 序扫这个区间,因为这个人有贡献的区间一定在这个人右边,所以处理到这个区间的时候,所有打在上面的 tag 已经处理完毕了

    于是我们可以把从这个叶子到根的 logn 层凸包并起来二分

    然后就写完了。。

    #include<bits/stdc++.h>
    #define LL long long
    #define DB long double
    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 = 152600;
    const DB inf = (DB)1.0 / 0.0;
    int n,l,r;
    LL a,b,c;
    LL sum[maxn],f[maxn];
    void force()
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=max(i-r,0);j<=min(i-l,n);j++)
                f[i] = max(f[i],f[j] + a * (sum[i] - sum[j]) * (sum[i] - sum[j]) + b * (sum[i] - sum[j]) + c);
        }
        cout<<f[n]<<endl;
    }
    struct point
    {
        DB x,y;
        int id;
        point operator - (const point &b)const{return (point){x - b.x,y - b.y,id};}
        bool operator < (const point &b)const{return x < b.x;}
        DB operator * (const point &b)const{return x * b.y - y * b.x;}
    }ps[maxn];
    vector<int> Items[maxn << 2],ch[maxn << 2];
    DB dp[maxn],Sum[maxn];
    int q[maxn],top;
    #define ls (x << 1)
    #define rs ((x << 1) | 1)
    void Insert(int x, int l, int r, int L, int R, int id)
    {
        if(L <= l && r <= R){Items[x].push_back(id);return;}
        int mid = (l + r) >> 1;
        if(L <= mid) Insert(ls,l,mid,L,R,id);
        if(R > mid) Insert(rs,mid + 1,r,L,R,id);
    }
    inline DB trans(int frm,int targ){return (DB)dp[frm] + (DB)a * (Sum[targ] - Sum[frm]) * (Sum[targ] - Sum[frm]) + (DB)b * (Sum[targ] - Sum[frm]) + c;}
    void CDQ(int x,int l,int r)
    {
        int tp = 0;
        for(int i=0;i<Items[x].size();i++)
        {
            int to = Items[x][i];
            ps[++tp] = (point){Sum[to],dp[to] + (DB)a * Sum[to] * Sum[to] - (DB)b * Sum[to],to};
        }
        if(tp)
        {
            sort(ps + 1,ps + tp + 1);top = 0;
            q[++top] = 1;
            for(;q[1] <= tp && ps[q[1]].y == -inf;++q[1]);        
            for(int i=q[1]+1;i<=tp;i++)
            {
                if(ps[i].y == -inf) continue;
                for(;top > 1 && ((ps[q[top]] - ps[q[top-1]]) * (ps[i] - ps[q[top]])) >= 0;--top); 
                q[++top] = i;
            }
            if(q[1] > tp)top = 0;
            for(;top && ps[q[top]].y == -inf;--top);
            if(top)for(int i=1;i<=top;i++)ch[x].push_back(ps[q[i]].id);
        }
        if(l == r)
        {
            int now = x;
            while(now)
            {
                int nl = 0,nr = ch[now].size() - 1;
                while(nl < nr)
                {
                    int md = (nl + nr) >> 1;
                    if(md < (ch[now].size() - 1) && trans(ch[now][md],l) <= trans(ch[now][md+1],l))nl = md + 1;
                    else nr = md;
                }
                if(ch[now].size())dp[l] = max(dp[l],trans(ch[now][nl],l));
                now = now >> 1;
            }
            return;
        }
        int mid = (l + r) >> 1;
        CDQ(ls,l,mid);CDQ(rs,mid + 1,r);
    }
    void solve()
    {
        for(int i=1;i<=n;i++)Sum[i] = sum[i];
        for(int i=1;i<=n;i++)dp[i] = -inf;
        for(int i=0;i<=n;i++)Insert(1,0,n,i + l,min(i+r,n),i);
        //cout<<111<<endl;
        CDQ(1,0,n);LL ans = floor(dp[n]);
        printf("%lld
    ",ans);
        //cout<<ret<<endl;
    }
    int main()
    {
        freopen("paint.in","r",stdin);
        freopen("paint.out","w",stdout);
        n = read();a = read();b = read();c = read();l = read();r = read();
        for(int i=1;i<=n;i++)sum[i] = sum[i - 1] + read();
        //if(n <= 1000)force();
        //else
        solve();
    }
    T2

    T3 route

    树上找一条链,满足

    1.链上最长边和最短边的差不超过 m

    2.链上最短边 $ imes$ 链上边权和 最大

    $n,m leq 2 imes 10^5$

    sol:

    如果没有 m 的限制,就是一个点分治

    对于分治中心往下的每一条链,我们记一个信息 $(mi,su)$ 表示这条链上最小值为 $mi$ ,权值和为 $su$

    按 $mi$ 的下标插入树状数组,每次查询即可

    注意每次按儿子顺序正着做一遍,反着做一遍·

    这样写可以 $O(nlog^2n)$ 拿到 80 分

    或者我们用 LCT 维护子树信息,很好想但。。。不太可写

    正解基于下面一个结论:如果树上一个连通块 u 的一条直径为 $u_i,u_j$ ,一个连通块 v 的一条直径为 $v_i,v_j$

    则 $(u_i,v_i),(u_i,v_j),(u_j,v_i),(u_j,v_j)$ 其中至少一个为连通块 u,v 的并集的直径

    于是对于 80 分我们可以按从大到小加入每条边然后直接统计答案

    对于 100 分,除了加入,我们还有删除操作

    这个时候我们可以线段树按时间分治,用一个可撤销的并查集来维护连通性

    或者 LCT

    #include<cstdio>
    #include<cctype>
    #include<queue>
    #include<ctime>
    #include<cstring>
    #include<algorithm>
    #define dwn(i,s,t) for(int i=s;i>=t;i--)
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define ren for(int i=first[x];i;i=next[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;
    }
    typedef long long ll;
    typedef pair<int,int> Pair;
    const int maxn=160010;
    struct Edge {
        int u,v,w;
        bool operator < (const Edge& ths) const {return w>ths.w;}
    }E[maxn];
    int n,lim,first[maxn],next[maxn<<1],dis[maxn<<1],to[maxn<<1],e;
    void AddEdge(int u,int v,int w) {
        to[++e]=v;dis[e]=w;next[e]=first[u];first[u]=e;
        to[++e]=u;dis[e]=w;next[e]=first[v];first[v]=e;
    }
    ll mn[20][maxn*2],dep[maxn];
    int pos[maxn*2],cnt;
    void dfs(int x,int fa) {
         mn[0][++cnt]=dep[x];pos[x]=cnt;
         ren if(to[i]!=fa) {
             dep[to[i]]=dep[x]+dis[i];
             dfs(to[i],x);    
             mn[0][++cnt]=dep[x];
         }
    }
    int Log[maxn*2];
    void init() {
         Log[0]=-1;rep(i,1,cnt) Log[i]=Log[i>>1]+1;
         for(int j=1;(1<<j)<=cnt;j++)
            for(int i=1;i+(1<<j)-1<=cnt;i++)
               mn[j][i]=min(mn[j-1][i],mn[j-1][i+(1<<j-1)]);     
    }
    ll query(int x,int y) {
        ll ans=dep[x]+dep[y];x=pos[x];y=pos[y];if(x>y) swap(x,y);
        int k=Log[y-x+1];
        return ans-2*min(mn[k][x],mn[k][y-(1<<k)+1]);    
    }
    Pair A[maxn];
    Pair merge(Pair A,Pair B) {
        int x1=A.first,y1=A.second;
        int x2=B.first,y2=B.second;
        int x=x1,y=y1;
        if(query(x2,y2)>query(x,y)) x=x2,y=y2;
        if(query(x2,y1)>query(x,y)) x=x2,y=y1;
        if(query(x2,x1)>query(x,y)) x=x2,y=x1;
        if(query(y2,x1)>query(x,y)) x=y2,y=x1;
        if(query(y2,y1)>query(x,y)) x=y2,y=y1;
        return make_pair(x,y);
    }
    int pa[maxn],size[maxn];
    int findset(int x) {return x==pa[x]?x:findset(pa[x]);}
    int end[maxn];
    struct Data {
        int x,y,pax,sizey;
        ll ansv;
        Pair datay;
    }S[maxn];
    int ToT;
    ll maxlen,ans;
    void link(int x,int y) {
        x=findset(x);y=findset(y);
        if(x==y) return;
        if(size[x]>size[y]) swap(x,y);
        S[++ToT]=(Data){x,y,pa[x],size[y],maxlen,A[y]};
        pa[x]=y;if(size[x]==size[y]) size[y]++;A[y]=merge(A[y],A[x]);
        maxlen=max(maxlen,query(A[y].first,A[y].second));
    }
    void restore(int begin) {
        while(ToT!=begin) {
            int x=S[ToT].x,y=S[ToT].y,pax=S[ToT].pax,sizey=S[ToT].sizey;
            maxlen=S[ToT].ansv;Pair z=S[ToT--].datay;
            pa[x]=pax;size[y]=sizey;A[y]=z;
        }
    }
    int ls[maxn<<1],rs[maxn<<1],rt;
    void buildtree(int& o,int l,int r) {
        o=++ToT;if(l==r) return;
        int mid=l+r>>1;
        buildtree(ls[o],l,mid);buildtree(rs[o],mid+1,r);
    }
    int first2[maxn<<1],nxt2[maxn*20],id[maxn*20];
    void AddMark(int x,int val) {
        id[++cnt]=val;nxt2[cnt]=first2[x];first2[x]=cnt;
    }
    void query(int o,int l,int r,int ql,int qr,int val) {
        if(ql<=l&&r<=qr) AddMark(o,val);
        else {
            int mid=l+r>>1;
            if(ql<=mid) query(ls[o],l,mid,ql,qr,val);
            if(qr>mid) query(rs[o],mid+1,r,ql,qr,val);
        }
    }
    void solve(int o,int l,int r) {
        int begin=ToT;
        for(int i=first2[o];i;i=nxt2[i]) link(E[id[i]].u,E[id[i]].v);
        if(l<r) {
            int mid=l+r>>1;
            solve(ls[o],l,mid);
            solve(rs[o],mid+1,r);
        }
        else ans=max(ans,maxlen*E[l].w);
        restore(begin);
    }
    int main() {
        freopen("route.in","r",stdin);
        freopen("route.out","w",stdout);
        n=read();lim=read();
        rep(i,1,n-1) {
            E[i].u=read();E[i].v=read();E[i].w=read();
            AddEdge(E[i].u,E[i].v,E[i].w);
        }
        buildtree(rt,1,n-1);ToT=0;
        dfs(1,0);init();cnt=0;
        sort(E+1,E+n);
        rep(i,1,n) pa[i]=i,A[i]=make_pair(i,i),size[i]=1;
        int j=n-1;
        dwn(i,n-1,1) {
            while(E[i].w-E[j].w>lim) j--;
            end[i]=j;query(rt,1,n-1,i,end[i],i);
        }
        solve(rt,1,n-1);
        printf("%lld
    ",ans);
        return 0;
    }
    T3
  • 相关阅读:
    PHP Mail 简介
    二级叉的结算另一种方法
    PHP extract() 函数
    array_count_values函数
    对碰结算的方法
    array_walk() 函数
    函数引发二级叉的结算
    array_intersect() php筛选两个数组共有的元素
    php文件锁
    JAVA Math常用方法
  • 原文地址:https://www.cnblogs.com/Kong-Ruo/p/10022768.html
Copyright © 2011-2022 走看看