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;
}
posted @ 2020-06-27 19:28  JSOI爆零珂学家yzhang  阅读(585)  评论(4编辑  收藏  举报