2020各省省選試題選做及簡要題解


聯考B卷

Day1T1卡牌游戲

把前綴和中正的加起來即可,復雜度為\(O(N)\)(難道這題都不會的能去sx?)

//μ's forever
#include <bits/stdc++.h>
#define N 100005
#define ll long long
#define getchar nc
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
    register int x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*f;
}
inline void write(register ll x)
{
    if(!x)putchar('0');if(x<0)x=-x,putchar('-');
    static int sta[20];register int tot=0;
    while(x)sta[tot++]=x%10,x/=10;
    while(tot)putchar(sta[--tot]+48);
}
int n,a[N];
ll sum[N],ans;
int main()
{
    n=read();
    for(register int i=1;i<=n;++i)
        a[i]=read(),sum[i]=0ll+sum[i-1]+a[i];
    for(register int i=2;i<=n;++i)
        if(sum[i]>0)
            ans+=sum[i];
    write(ans);
	return 0;
}

Day1T2消息傳遞

比較裸的一個點分治,每次分治記錄\(p[i]\)表示到分治中心(重心)距離為\(i\)的點的個數,對答案的貢獻計算也非常simple,所有長度為k-dep[x]的點數減去在同一子樹里的,復雜度為\(O(Tn\log n)\)

//μ's forever
#include <bits/stdc++.h>
#define N 100005
#define getchar nc
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
    register int x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*f;
}
inline void write(register int x)
{
    if(!x)putchar('0');if(x<0)x=-x,putchar('-');
    static int sta[20];register int tot=0;
    while(x)sta[tot++]=x%10,x/=10;
    while(tot)putchar(sta[--tot]+48);
}
struct edge{
    int to,nxt;
}e[N<<1];
int head[N],cnt=0;
inline void add(register int u,register int v)
{
    e[++cnt]=(edge){v,head[u]};
    head[u]=cnt;
}
int T,n,m,ans[N],f[N],rt,siz[N],ms,vis[N],dep[N],sta[N],top=0,st[N],ed[N],p[N];
vector<pair<int,int> > q[N];
inline void getrt(register int x,register int fa)
{
    f[x]=0,siz[x]=1;
    for(register int i=head[x];i;i=e[i].nxt)
    {
        int v=e[i].to;
        if(vis[v]||v==fa)
            continue;
        getrt(v,x);
        f[x]=max(f[x],siz[v]);
        siz[x]+=siz[v];
    }
    f[x]=max(f[x],ms-siz[x]);
    if(f[x]<f[rt])
        rt=x;
}
inline void dfs(register int x,register int fa)
{
    sta[++top]=x;
    st[x]=top;
    for(register int i=head[x];i;i=e[i].nxt)
    {
        int v=e[i].to;
        if(v==fa||vis[v])
            continue;
        dep[v]=dep[x]+1;
        dfs(v,x);
    }
    ed[x]=top;
}
inline void solve(register int x)
{
    vis[x]=1,dep[x]=0;
    dfs(x,0);
    for(register int i=1;i<=top;++i)
        ++p[dep[sta[i]]];
    for(register int i=0;i<q[x].size();++i)
        ans[q[x][i].second]+=p[q[x][i].first];
    for(register int i=head[x];i;i=e[i].nxt)
    {
        int v=e[i].to;
        if(vis[v])
            continue;
        for(register int j=st[v];j<=ed[v];++j)
            --p[dep[sta[j]]];
        for(register int j=st[v];j<=ed[v];++j)
            for(register int k=0;k<q[sta[j]].size();++k)
                if(q[sta[j]][k].first>=dep[sta[j]])
                    ans[q[sta[j]][k].second]+=p[q[sta[j]][k].first-dep[sta[j]]];
        for(register int j=st[v];j<=ed[v];++j)
            ++p[dep[sta[j]]];
    }
    while(top)
        --p[dep[sta[top--]]];
    for(register int i=head[x];i;i=e[i].nxt)
    {
        int v=e[i].to;
        if(vis[v])
            continue;
        rt=0;
        ms=siz[v];
        getrt(v,0);
        solve(rt);
    }
}
int main()
{
    T=read();
    while(T--)
    {
        memset(head,0,sizeof(head));
        memset(ans,0,sizeof(ans));
        memset(vis,0,sizeof(vis));
        memset(ans,0,sizeof(ans));
        cnt=0;
        n=read(),m=read();
        for(register int i=1;i<n;++i)
        {
            int u=read(),v=read();
            add(u,v),add(v,u);
        }
        for(register int i=1;i<=m;++i)
        {
            int x=read(),k=read();
            q[x].push_back(make_pair(k,i));
        }
        rt=0;
        f[rt]=ms=n;
        getrt(1,0);
        solve(rt);
        for(register int i=1;i<=m;++i)
            write(ans[i]),puts("");
        for(register int i=1;i<=n;++i)
            q[i].clear();
    }
	return 0;
}

Day1T3冰火戰士

發現隨着溫度的升高,可用冰戰士能量之和不降,可用火戰士能量之和不升

題目要求的是兩者中較小值的兩倍,那么顯然兩函數交點最優

求交點不難想到做差,然后再數據結構上查零點(此處零點指當\(ice<fire/ice>fire\)時,\(fire-ice/ice-fire\)最小值的點)

普通的二分零點再判斷的算法是\(O(n \log^2 n)\)的,並不能通過此題

這里應該通過倍增的手法來找到這個零點,從0的位置開始,每次加\(2^n,2^{n-1},……,2^0\),加上的正好就是新值的\(lowbit\),這樣可以\(O(1)\)更新差值並判斷

這樣就能在\(O(n\log n)\)時間內解決本題

//μ's forever
#include <bits/stdc++.h>
#define N 2000005
#define getchar nc
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
    register int x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*f;
}
inline void write(register int x)
{
    if(!x)putchar('0');if(x<0)x=-x,putchar('-');
    static int sta[20];register int tot=0;
    while(x)sta[tot++]=x%10,x/=10;
    while(tot)putchar(sta[--tot]+48);
}
struct bit{
    int ic[N],fr[N],sz,del=0;
    inline void modifyic(register int x,register int val)
    {
        for(register int i=x;i<=sz;i+=(i&(-i)))
            ic[i]+=val;
    }
    inline void modifyfr(register int x,register int val)
    {
        del+=val;
        for(register int i=x+1;i<=sz;i+=(i&(-i)))
            fr[i]-=val;
    }
    inline int qrym(register int x)
    {
        int sic=0,sfr=del;
        for(register int i=x;i;i-=(i&(-i)))
            sic+=ic[i],sfr+=fr[i];
        return min(sic,sfr);
    }
    inline int fd1()
    {
        int res=0,dis=-del;
        for(register int i=20;i>=0;--i)
        {
            if(res+(1<<i)>sz)
                continue;
            int tmp=dis+ic[res+(1<<i)]-fr[res+(1<<i)];
            if(tmp<0)
            {
                dis=tmp;
                res+=(1<<i);
            }
        }
        return res;
    }
    inline int fd2(register int exp)
    {
        int res=0,sic=0,sfr=del;
        for(register int i=20;i>=0;--i)
        {
            if(res+(1<<i)>sz)
                continue;
            int tmp1=sic+ic[res+(1<<i)],tmp2=sfr+fr[res+(1<<i)];
            if(tmp1<tmp2)
            {
                res+=(1<<i);
                sic=tmp1,sfr=tmp2;
            }
            else if(min(tmp1,tmp2)==exp)
            {
                res=res+(1<<i);
                sic=tmp1,sfr=tmp2;
            }
        }
        return res;
    }
}tr;
struct node{
    int ty,tp,en;
}q[N];
int Q,val[N],cnt=0;
int main()
{
    Q=read();
    for(register int i=1;i<=Q;++i)
    {
        int x=read();
        if(x==1)
        {
            q[i].ty=read(),q[i].tp=read(),q[i].en=read();
            val[++cnt]=q[i].tp;
        }
        else
        {
            int k=read();
            q[i].ty=q[k].ty,q[i].tp=q[k].tp,q[i].en=-q[k].en;
        }
    }
    sort(val+1,val+cnt+1);
    int ns=unique(val+1,val+cnt+1)-val-1;
    tr.sz=ns;
    for(register int i=1;i<=Q;++i)
        q[i].tp=lower_bound(val+1,val+ns+1,q[i].tp)-val;
    for(register int i=1;i<=Q;++i)
    {
        if(q[i].ty==0)
            tr.modifyic(q[i].tp,q[i].en);
        else
            tr.modifyfr(q[i].tp,q[i].en);
        pair<int,int> ans;
        int ip1=tr.fd1();
        ans=make_pair(tr.qrym(ip1),ip1);
        if(ip1<ns)
        {
            int exp=tr.qrym(ip1+1);
            if(exp>=ans.first)
            {
                int ip2=tr.fd2(exp);
                ans=make_pair(exp,ip2);
            }
        }
        if(ans.first==0)
            puts("Peace");
        else
            write(val[ans.second]),putchar(' '),write(ans.first<<1),puts("");
    }
	return 0;
}

Day2T1幸運數字

離散化后進行差分,在前綴異或和上找答案,時間復雜度為\(O(n \log n)\)

//μ's forever
#include <bits/stdc++.h>
#define N 100005
#define getchar nc
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
    register int x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*f;
}
inline void write(register int x)
{
    if(!x)putchar('0');if(x<0)x=-x,putchar('-');
    static int sta[20];register int tot=0;
    while(x)sta[tot++]=x%10,x/=10;
    while(tot)putchar(sta[--tot]+48);
}
struct node{
    int tp,a,b,c;
}q[N];
int n,val[N<<1],cnt,tg[N<<1],ans,cur;
int main()
{
    n=read();
    val[++cnt]=-2000000000;
    val[++cnt]=0;
    for(register int i=1;i<=n;++i)
    {
        q[i].tp=read();
        if(q[i].tp==1)
        {
            q[i].a=read(),q[i].b=read(),q[i].c=read();
            val[++cnt]=q[i].a,val[++cnt]=q[i].b+1;
        }
        else
        {
            q[i].a=read(),q[i].b=read();
            val[++cnt]=q[i].a,val[++cnt]=q[i].a+1;
        }
    }
    sort(val+1,val+1+cnt);
    int sz=unique(val+1,val+1+cnt)-val-1;
    for(register int i=1;i<=n;++i)
    {
        if(q[i].tp==1)
        {
            int posa=lower_bound(val+1,val+sz+1,q[i].a)-val,posb=lower_bound(val+1,val+sz+1,q[i].b+1)-val;
            tg[posa]^=q[i].c,tg[posb]^=q[i].c;
        }
        else
        {
            int posa=lower_bound(val+1,val+sz+1,q[i].a)-val,posb=posa+1;
            tg[posa]^=q[i].b,tg[posb]^=q[i].b;
            if(q[i].tp==3)
                tg[1]^=q[i].b;
        }
    }
    for(register int i=1,tmp=0;i<=sz;++i)
    {
        tmp^=tg[i];
        if(tmp>cur)
        {
            cur=tmp;
            if(val[i]<0)
                ans=val[i+1]-1;
            else
                ans=val[i];
        }
        else if(tmp==cur)
        {
            if(val[i]<0)
                ans=val[i+1]-1;
            else if(val[i]<=fabs(ans))
                ans=val[i];
        }
    }
    printf("%d %d\n",cur,ans);
	return 0;
}

Day2T2信號傳遞

\(cnt[i][j]\)表示從\(i\)\(j\)共有幾個

考慮狀壓dp,設\(f[S]\)表示用了\(S\)集合里的數,考慮了編號為前\(|S|\)的信號站

考慮第\(pos=|S|+1\)個位置,選用了一個沒選過的數\(i\),經過推柿子,可以得到一下轉移

\[f[S|(1<<i)]<-f[S]+pos*\sum_{j \in s,j<i} (k*cnt[i][j]+cnt[j][i])+pos*\sum_{j \in s, j>i}{-cnt[i][j]+k*cnt[j][i]} \]

這是個\(O(2^m m^2)\)的暴力,考慮優化

我們發現后面這一大串實際珂以預處理,又其他狀態快速轉移得來,得到一個\(O(2^m m)\)的做法,可惜這樣會MLE

這里dyls教了我一個手法,用隊列來滾動優化,這樣就得到了\(O(2^m m)\)的正解

//μ's forever
#include <bits/stdc++.h>
#define N 100005
#define M 23
#define getchar nc
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
    register int x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*f;
}
inline void write(register int x)
{
    if(!x)putchar('0');if(x<0)x=-x,putchar('-');
    static int sta[20];register int tot=0;
    while(x)sta[tot++]=x%10,x/=10;
    while(tot)putchar(sta[--tot]+48);
}
struct node{
    int sta,v[M];
};
queue<node> q;
int n,m,k,s[N],cnt[M][M];
int bitc[1<<M],id[1<<M],f[1<<M];
int main()
{
    n=read(),m=read(),k=read();
    for(register int i=1;i<=n;++i)
        s[i]=read()-1;
    for(register int i=1;i<n;++i)
        ++cnt[s[i]][s[i+1]];
    int Ms=(1<<m)-1;
    for(register int i=0;i<m;++i)
        id[1<<i]=i;
    for(register int i=1;i<=Ms;++i)
        bitc[i]=bitc[i>>1]+(i&1),f[i]=1145141919;
    node tmp;
    tmp.sta=0;
    for(register int i=0;i<m;++i)
    {
        tmp.v[i]=0;
        for(register int j=0;j<m;++j)
            if(j!=i)
                tmp.v[i]+=k*cnt[j][i]-cnt[i][j];
    }
    q.push(tmp);
    while(!q.empty())
    {
        node a=q.front();
        q.pop();
        int nxtbitc=bitc[a.sta]+1;
        for(register int i=a.sta^Ms;i;i-=i&(-i))
        {
            int nf=f[a.sta],nsta=a.sta|(i&(-i));
            nf+=nxtbitc*a.v[id[i&(-i)]];
            f[nsta]=min(f[nsta],nf);
        }
        for(register int i=0;i<m;++i)
        {
            if((a.sta>>i)&1)
                break;
            node nsta;
            nsta.sta=a.sta|(1<<i);
            for(register int j=nsta.sta^Ms;j;j-=j&(-j))
                nsta.v[id[j&(-j)]]=a.v[id[j&(-j)]]+k*cnt[id[j&(-j)]][i]+cnt[i][id[j&(-j)]]-k*cnt[i][id[j&(-j)]]+cnt[id[j&(-j)]][i];
            q.push(nsta);
        }
    }
    printf("%d\n",f[Ms]);
	return 0;
}

Day2T3丁香之路

考慮每次走的路線,應該是必走的邊和自己添加的一些邊的"歐拉路",起點和終點為奇點,其他點為偶點(先這樣理解,此處歐拉路只是個廣義的理解,不是真正的歐拉路)

程序中把起點和終點度數加一,再按順序匹配相鄰的奇點,給答案加上這條邊,這樣就結束了?

還沒完,這個圖只是定義上滿足歐拉路,但不保證連通。用並查集維護連通塊,之前連接的兩奇點與之間偶點能合並成一個連通塊。我們對有的連通塊之間求距離,找出最小生成樹,給答案加上邊權和的兩倍,顯然這里能成為最小生成樹的邊是編號相鄰兩點間的

這樣我們就在\(O((m+n^2) \log n)\)的時間內解決了問題

//μ's forever
#include <bits/stdc++.h>
#define ll long long
#define N 2505
#define getchar nc
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
    register int x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*f;
}
inline void write(register ll x)
{
    if(!x)putchar('0');if(x<0)x=-x,putchar('-');
    static int sta[20];register int tot=0;
    while(x)sta[tot++]=x%10,x/=10;
    while(tot)putchar(sta[--tot]+48);
}
int n,m,s,fa[N],in[N];
vector<int> v[N];
inline int find(register int x)
{
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
struct node{
    int u,v,w;
    bool operator < (const node &a) const{
        return w<a.w;
    }
};
int main()
{
    n=read(),m=read(),s=read();
    ll sum=0;
    for(register int i=1;i<=n;++i)
        fa[i]=i;
    for(register int i=1;i<=m;++i)
    {
        int s=read(),t=read();
        v[s].push_back(t),v[t].push_back(s);
        sum+=fabs(s-t);
        fa[find(s)]=find(t);
    }
    for(register int i=1;i<=n;++i)
        in[i]=find(i);
    for(register int i=1;i<=n;++i)
    {
        for(register int j=1;j<=n;++j)
            fa[j]=j;
        v[s].push_back(i),v[i].push_back(s);
        int pre=0;ll ans=sum;
        for(register int j=1;j<=n;++j)
            if(v[j].size()&1)
            {
                if(pre)
                {
                    ans+=j-pre;
                    for(register int k=pre;k<j;++k)
                        fa[find(in[k])]=find(in[j]);
                    pre=0;
                }
                else
                    pre=j;
            }
        vector<node> e;
        for(register int j=1;j<=n;++j)
            if(v[j].size())
            {
                if(pre&&find(in[j])!=find(in[pre]))
                    e.push_back((node){in[j],in[pre],j-pre});
                else
                    pre=j;
            }
        sort(e.begin(),e.end());
        for(register int j=0;j<e.size();++j)
            if(find(e[j].u)!=find(e[j].v))
                fa[find(e[j].u)]=e[j].v,ans+=2*e[j].w;
        write(ans),putchar(' ');
        v[s].pop_back(),v[i].pop_back();
    }
    return 0;
}


免責聲明!

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



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