第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

確定一個\(\theta\)后,小於\(\theta\)的人會掛科,大於等於的不會掛科
繼續想下去,排序以后,枚舉\(\theta\),則掛科的人組成一個前綴,不掛科的人是一個后綴
因此我們只需快速統計前后綴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\n");else printf("N\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\n",res);
        }
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM