• 第21次CSP认证 题解


    A

    按照题意直接求和即可

    #include<bits/stdc++.h>
    #define N 1100000
    #define db double
    #define ll long long 
    #define ldb long double
    #define ull unsigned long long
    using namespace std;
    const int h=3,ki=149,mo=998244353;
    int mod(int x){return (x%mo+mo)%mo;}
    int inc(int x,int k){x+=k;return x<mo?x:x-mo;}
    int dec(int x,int k){x-=k;return x>=0?x:x+mo;}
    int read(){int x;scanf("%d",&x);return x;}
    void write(int x){printf("%d",x);}
    int main()
    {
        int n=read();
        ll ans=0;
        for(int i=1;i<=n;i++)ans+=1ll*read()*read();
        ans=max(ans,0ll);
        printf("%lld",ans);
        return 0;
    }
    

    B

    确定一个( heta)后,小于( heta)的人会挂科,大于等于的不会挂科
    继续想下去,排序以后,枚举( heta),则挂科的人组成一个前缀,不挂科的人是一个后缀
    因此我们只需快速统计前后缀0的个数和1的个数即可
    这里我采用了前缀和的方式来实现

    #include<bits/stdc++.h>
    #define N 1100000
    #define db double
    #define ll long long 
    #define ldb long double
    #define ull unsigned long long
    using namespace std;
    const int h=3,ki=149,mo=998244353;
    int mod(int x){return (x%mo+mo)%mo;}
    int inc(int x,int k){x+=k;return x<mo?x:x-mo;}
    int dec(int x,int k){x-=k;return x>=0?x:x+mo;}
    int read(){int x;scanf("%d",&x);return x;}
    void write(int x){printf("%d",x);}
    struct node
    {
        int x,flag;
    }p[N];
    bool cmp(node a,node b){return a.x<b.x;}
    int pre[N],suf[N];//numbers
    int main()
    {
        int n=read();
        for(int i=1;i<=n;i++)p[i].x=read(),p[i].flag=read();
        sort(p+1,p+n+1,cmp);
        for(int i=1;i<=n;i++)pre[i]=pre[i-1]+(p[i].flag==0);
        for(int i=n;i>=1;i--)suf[i]=suf[i+1]+(p[i].flag==1);
        int t=0,ans=-mo;
        for(int i=1;i<=n;i++)
        if(i==1||p[i].x!=p[i-1].x)
        {
            if(pre[i-1]+suf[i]>=ans)
            {
                ans=pre[i-1]+suf[i];
                t=p[i].x;
            }
        }   
        printf("%d",t);
        return 0;
    }
    

    C

    50pts的暴力
    没啥好说的
    按照题意模拟即可
    大概有空会更一下正解qwq

    #include<bits/stdc++.h>
    #define N 1100000
    #define db double
    #define int long long 
    #define ldb long double
    #define ull unsigned long long
    using namespace std;
    const int h=3,ki=149,mo=1e9+7;
    int mod(int x){return (x%mo+mo)%mo;}
    int inc(int x,int k){x+=k;return x<mo?x:x-mo;}
    int dec(int x,int k){x-=k;return x>=0?x:x+mo;}
    int read(){int x;scanf("%lld",&x);return x;}
    struct node
    {
        int flag,sz;
    }f[N];
    int rt,size;
    map<string,int>mp[N];
    bool insert()
    {
        string s,to;
        cin>>s;
        int x=rt,n=s.size(),value=read();
        for(int i=0;i<n;i++)
        {
            if(s[i]=='/')
            {
                if(!i)continue;
                if(mp[x][to]){if(f[mp[x][to]].flag==1)return false;}
                if(!mp[x][to]){mp[x][to]=++size;f[mp[x][to]].flag=0;}
                x=mp[x][to];
                to.clear();
                continue;
            }
            to=to+s[i];
        }
        if(mp[x][to]){if(f[mp[x][to]].flag==0)return false;}
        if(!mp[x][to]){mp[x][to]=++size;f[mp[x][to]].flag=1;}
        x=mp[x][to];f[x].sz=value;
        return true;
    }
    bool delate()
    {
        string s,to;
        cin>>s;
        int x=rt,n=s.size();
        for(int i=0;i<n;i++)
        {
            if(s[i]=='/')
            {
                if(!i)continue;
                if(!mp[x][to])return true;
                x=mp[x][to];
                to.clear();
                continue;
            }
            to=to+s[i];
        }
        if(!mp[x][to])return true;
        mp[x].erase(to);
        return true;
    }
    void print(bool flag){if(flag)printf("Y
    ");else printf("N
    ");}
    signed main()
    {
        int qnum=read();rt=1;size=1;
        for(int i=1;i<=qnum;i++)
        {
            char fg[3];
            scanf("%s",fg);
            if(fg[0]=='C')print(insert());
            if(fg[0]=='R')print(delate());
        }
        return 0;
    }
    

    D

    题意:
    一颗树,给(k)个点集,让你选(m)个起点,(k)辆车分别从中任选一个起点,同时出发,遍历自己对应的点集,最小化最大的时间
    (n<=100)
    (m<=10)
    (k<=10)

    题解:
    然后有(70pts)的部分分是(m=k)
    这个时候车之间显然独立,都可以选择对自己而言最优的起点
    因此暴力枚举起点,算它遍历点集的时间。
    遍历点集的最短时间就是按照dfs序遍历,然后删掉一个深度最深的回来的时间即可(因为最后无需回到起点,可以留在深度最深的那个点
    然后每辆车选则最优的起点即可。

    考虑正解
    发现要做的就是选出(m)个起点,每个起点覆盖一个以点集为基本元素的集合,问覆盖整个大小为(k)的集合的最小代价
    考虑dp
    (dp[i][j][s])表示考虑前(i)个点,选了(j)个作为起点,已覆盖了(s)这个集合的最小时间。
    转移的时候枚举第i+1个起点覆盖那些集合
    这样的话转移的复杂度是指数级别的
    但是由于贡献是一个(max)的形式,
    选择了花费时间为(x)的一个元素,一定可以把所有(<=x)的元素一块选上
    也就是说,排序后只会取一个前缀
    这样就可以(o(k))转移了。
    因此总复杂度(O(n*m*k*2^k))

    #include<bits/stdc++.h>
    #define N 550
    #define M 220
    #define T 2200
    #define db double
    #define int long long 
    #define ldb long double
    #define ull unsigned long long
    using namespace std;
    const int h=3,ki=149,inf=1e15,mo=998244353;
    int mod(int x){return (x%mo+mo)%mo;}
    int inc(int x,int k){x+=k;return x<mo?x:x-mo;}
    int dec(int x,int k){x-=k;return x>=0?x:x+mo;}
    int read(){int x;scanf("%lld",&x);return x;}
    struct edge{int to,nxt,w;}e[T*2];
    int num,head[T];
    void add(int x,int y,int z){e[++num]={y,head[x],z};head[x]=num;}
    bool flag[N][N];
    int  tot=0,sz[T],dep[T];
    void prepare(int x,int fa)
    {
        for(int i=head[x];i!=-1;i=e[i].nxt)
        {
            int to=e[i].to;
            if(to==fa)continue;
            prepare(to,x);
            sz[x]+=sz[to];
            dep[x]=max(dep[x],dep[to]+e[i].w);
        }
    }
    void dfs(int x,int fa)
    {
        for(int i=head[x];i!=-1;i=e[i].nxt)
        {
            int to=e[i].to;
            if(to==fa)continue;
            if(sz[to])
            {
                tot+=e[i].w;
                dfs(to,x);
                tot+=e[i].w;
            }
        }
    }
    struct node{int x,k;}w[N][N];
    bool cmp(node a,node b){return a.k<b.k;}
    int dp[150][22][2200];
    signed main()
    {
        int n=read(),m=read(),k=read();
        num=-1;memset(head,-1,sizeof(head));
        for(int i=1;i<=n;i++)for(int j=1;j<=k;j++)flag[i][j]=read();
        for(int i=1;i<n;i++){int x=read(),y=read(),z=read();add(x,y,z);add(y,x,z);}
        for(int x=1;x<=n;x++)
        for(int i=1;i<=k;i++)
        {
            bool ok=false;
            for(int t=1;t<=n;t++)if(flag[t][i])sz[t]=1,dep[t]=0,ok=true;else sz[t]=0,dep[t]=-inf;
            if(!ok)
            {
                w[x][i]={i,0};
                continue;
            }
            prepare(x,x);tot=0;dfs(x,x);
            w[x][i]={i,tot-dep[x]};
        }
        for(int x=1;x<=n;x++)sort(w[x]+1,w[x]+k+1,cmp);
    
        for(int x=0;x<=n;x++)
        for(int i=0;i<=m+1;i++)
        for(int s=0;s<(1<<k);s++)
        dp[x][i][s]=inf;
    
        dp[0][0][0]=0;
        for(int x=0;x<n;x++)
        for(int i=0;i<=m;i++)
        for(int s=0;s<(1<<k);s++)
        {
            int o=dp[x][i][s];
            dp[x+1][i][s]=min(dp[x+1][i][s],o);
            for(int p=1,t=0;p<=k;p++)
            {
                t|=1<<(w[x+1][p].x-1);
                dp[x+1][i+1][s|t]=min(dp[x+1][i+1][s|t],max(o,w[x+1][p].k));
            }
        }
        int ans=inf;
        for(int x=1;x<=n;x++)
        for(int i=0;i<=m;i++)
        ans=min(ans,dp[x][i][(1<<k)-1]);
        printf("%lld",ans);
        return 0;
    }
    

    E

    题意:
    维护(n)个初始为(0)的三元组
    (m)次操作,支持区间乘,区间加,区间三元组置换,区间求和
    (n<=1e9)
    (m<=5e4)
    题解:

    考虑线段树
    如果没有三元组置换这个操作的话,就是这个下面这个题+动态开点

    https://www.luogu.com.cn/problem/P3373

    不再赘述

    多加一个操作的话,其实想法是一样的。

    这里详细说一下这种多修改的线段树怎么做

    我们需要明白的是,这种东西,一定要规定好各个操作pushdown的顺序

    先pushdown的操作需要考虑对后pushdown操作的影响

    首先考虑只有乘法和加法

    我们设一个区间的区间和为s

    打标记的本质就是把一个区间描述成下面这个样子

    这种是先pushdown乘法标记的:add(mul(s,k1),k2)也就是(s*k1+k2)

    这个时候如果你给它乘上一个数(t)

    它应该变成(t*k1*s+t*k2),也就是(add(mul(s,t*k1),t*k2))

    因此,我们在pushdown乘法标记的时候需要给加法标记乘一个倍数

    这个时候如果你给它加上一个数(c)

    它应该变成(k1*s+k2+c),也就是(add(mul(s,k1),k2+c))

    因此,我们在pushdown加法标记的时候无需修改乘法标记

    那如果先pushdown加法标记呢?

    就会变成这个形式:mul(add(s,k1),k2),也就是((s+k1)*k2)

    这时如果加一个数c

    它应该变成((s+k1)*k2+c=(s+k1+c/k2)*k2),也就是(mul(add(s,k1+c/k2),k2))

    发现需要根据当前乘法标记的数值修改加法标记

    这时如果乘一个数t

    它应该变成((s+k1)*k2*t),也就是(mul(add(s,k1),k2*t))

    发现只需修改乘法标记即可

    理论上也是可以的,但是需要用到乘法逆元,比较麻烦,所以一般采用第一种写法

    好了

    回到这道题,我们要做的就是找到一个合适的,容易维护的顺序来pushdown

    经过手动枚举尝试,发现按照(mul,rev,add)的形式是最为合适的

    其中(rev(s,k),k=0,1,2)表示三元组循环移位(k)

    也就是说,我们把每一个区间(s)维护成下面这个形式

    (add(rev(mul(s,k1),k2),k3))

    或者这么写

    [k_a*a+t_a \ k_b*b+t_b \ k_c*c+t_c \ ]

    乘k后,发现对rev标记无影响,因为都会乘一个倍数,而加法标记应该乘上同样的倍数

    rev k后,发现会变成

    [k_b*b+t_b \ k_c*c+t_c \ k_a*a+t_a \ ]

    发现加法标记的顺序也应该变换一下

    最后pushdown加法标记即可

    代码如下

    #include<bits/stdc++.h>
    #define N 1100000
    #define M 5500000
    #define db double
    #define ll long long 
    #define ldb long double
    #define ull unsigned long long
    using namespace std;
    const int h=3,ki=149,mo=1e9+7;
    int mod(int x){return (x%mo+mo)%mo;}
    int inc(int x,int k){x+=k;return x<mo?x:x-mo;}
    int dec(int x,int k){x-=k;return x>=0?x:x+mo;}
    int read(){int x;scanf("%d",&x);return x;}
    void write(int x){printf("%d",x);}
    struct node{int x,y,z;};
    node operator+(node f,node g){return (node){inc(f.x,g.x),inc(f.y,g.y),inc(f.z,g.z)};}
    node operator*(node f,int k){return {1ll*f.x*k%mo,1ll*f.y*k%mo,1ll*f.z*k%mo};}
    node sp(node f,int k)
    {
        k%=3;
        if(k==0)return (node){f.x,f.y,f.z};
        if(k==1)return (node){f.y,f.z,f.x};
        if(k==2)return (node){f.z,f.x,f.y};
    }
    struct Segment_Tree
    {
        #define lson lc[o]
        #define rson rc[o]
        #define mid ((l+r)>>1)
        node f[M],addv[M];
        int size=0,lc[M],rc[M],mulv[M],revv[M];
        void insert(int &o){if(!o)o=++size,mulv[o]=1;}
        int pushup(int o){f[o]=f[lson]+f[rson];}
        void mul(int o,int k)
        {
            f[o]=f[o]*k;
            addv[o]=addv[o]*k;
            mulv[o]=1ll*mulv[o]*k%mo;
        }
        void rev(int o,int k)
        {
            f[o]=sp(f[o],k);
            addv[o]=sp(addv[o],k);
            revv[o]=(revv[o]+k)%3;
        }
        void add(int o,int l,int r,node k)
        {
            f[o]=f[o]+(k*(r-l+1));
            addv[o]=addv[o]+k;
        }
        void pushdown(int o,int l,int r)
        {
            insert(lson);mul(lson,mulv[o]);rev(lson,revv[o]);add(lson,l,mid,addv[o]);
            insert(rson);mul(rson,mulv[o]);rev(rson,revv[o]);add(rson,mid+1,r,addv[o]);
            mulv[o]=1;revv[o]=0;addv[o]=(node){0,0,0};
        }
        void optmul(int &o,int l,int r,int ql,int qr,int k)
        {
            insert(o);
            if(ql<=l&&r<=qr)return mul(o,k);
            pushdown(o,l,r);
            if(ql<=mid)optmul(lson,l,mid,ql,qr,k);
            if(qr>mid)optmul(rson,mid+1,r,ql,qr,k);
            pushup(o);
        }
        void optrev(int &o,int l,int r,int ql,int qr,int k)
        {
            insert(o);
            if(ql<=l&&r<=qr)return rev(o,k);
            pushdown(o,l,r);
            if(ql<=mid)optrev(lson,l,mid,ql,qr,k);
            if(qr>mid)optrev(rson,mid+1,r,ql,qr,k);
            pushup(o);
        }   
        void optadd(int &o,int l,int r,int ql,int qr,node k)
        {
            insert(o);
            if(ql<=l&&r<=qr)return add(o,l,r,k);
            pushdown(o,l,r);
            if(ql<=mid)optadd(lson,l,mid,ql,qr,k);
            if(qr>mid)optadd(rson,mid+1,r,ql,qr,k);
            pushup(o);
        }
        node query(int o,int l,int r,int ql,int qr)
        {
            if(!o)return (node){0,0,0};
            if(ql<=l&&r<=qr)return f[o];
            pushdown(o,l,r);
            node ans=(node){0,0,0};
            if(ql<=mid)ans=ans+query(lson,l,mid,ql,qr);
            if(qr>mid)ans=ans+query(rson,mid+1,r,ql,qr);
            return ans;
        }
    }T;
    int main()
    {
        int n=read(),qnum=read(),rt=0;
        for(int i=1;i<=qnum;i++)
        {
            int flag=read(),l=read(),r=read();
            if(flag==1)
            {
                node k;
                k.x=read();k.y=read();k.z=read();
                T.optadd(rt,1,n,l,r,k);
            }
            if(flag==2)
            {
                int k;
                k=read();
                T.optmul(rt,1,n,l,r,k);
            }
            if(flag==3)
            {
                T.optrev(rt,1,n,l,r,1);
            }
            if(flag==4)
            {
                node ans=T.query(rt,1,n,l,r);
                int res=0;
                res=inc(res,1ll*ans.x*ans.x%mo);
                res=inc(res,1ll*ans.y*ans.y%mo);
                res=inc(res,1ll*ans.z*ans.z%mo);
                printf("%d
    ",res);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    【SQL注入】之SQLMAP工具的使用
    【漏洞复现】之PHP-FRM远程代码执行漏洞(CVE-2019-11043)复现
    利用PHPStudy搭建伪静态页面
    【渗透测试小白系列】之利用PHPMyAdmin Getshell
    【渗透测试小白系列】之Docker入门以及漏洞环境搭建
    web服务器是什莫: tomcat各个目录简介
    equals和==的区别
    ln命令
    head tail ln sort uniq指令
    rpm包安装
  • 原文地址:https://www.cnblogs.com/Creed-qwq/p/14139097.html
Copyright © 2020-2023  润新知