Codeforces Round #664 (Div. 1) 部分简要题解

自闭了

A

所有的\(a_i\)可以根据是否\(\leq m\)分为两类,枚举使用了多少\(\leq m\)的数,\(>m\)的数的个数可以直接算出来。

int n,d,m,m1,m2,a[N],b[N];
ll s1[N],s2[N];

int main()
{
    n=read();d=read();m=read();
    rep(i,1,n)
    {
        int x=read();
        if (x>m) a[++m1]=x;
        else b[++m2]=x;
    }
    sort(a+1,a+1+m1);sort(b+1,b+1+m2);
    reverse(a+1,a+1+m1);reverse(b+1,b+1+m2);
    ll ans=0;
    rep(i,1,m1) s1[i]=s1[i-1]+a[i];
    rep(i,1,m2) s2[i]=s2[i-1]+b[i];
    rep(i,0,m2)
    {
        ll now=s2[i];
        int rst=(n-i-1)/(d+1)+1;
        //cout << i << " " << rst << endl;
        now+=s1[min(rst,m1)];
        ans=max(now,ans);
    }
    printf("%lld\n",ans);
    return 0;
}

B

考虑枚举\(k\)的全排列判断是否合法,注意到一个合法的\(c\)序列应满足选出来的边形成了若干个简单环,即所有点的入度和出度均为1。记\(cant_{i,j,p,q}\)表示当\(c_i=j,c_p=q\)时是否存在某个点的入度或者出度\(>1\).这个数组可以在保证\(c_i=j,c_p=q\)均合法时使用bitset加速判断。

int n,m,k,up,c[10],len[10][10],ans=0;
vector<pii> edge[10][10],sq[N];
bool cant[10][10][10][10],ban[10][10];
bitset<N> in[10][10],out[10][10];

void dfs(int dep,int sum)
{
    if (dep>k) {ans+=(sum==n);return;}
    rep(i,1,dep)
    {
        if (ban[dep][i]) continue;
        int ok=1;
        rep(j,1,dep-1)
        {
            if (cant[j][c[j]][dep][i]) {ok=0;break;}
        }
        if (!ok) continue;
        c[dep]=i;
        dfs(dep+1,sum+len[dep][i]);
    }
}

int main()
{
    n=read();m=read();k=read();
    rep(i,1,m)
    {
        int u=read()-1,v=read()-1,w=read();
        sq[u].pb(mkp(w,v));
    }
    rep(u,0,n-1)
    {
        int d=sq[u].size();
        sort(sq[u].begin(),sq[u].end());
        rep(j,1,d)
        {
            int v=sq[u][j-1].sec,w=sq[u][j-1].fir;
            edge[d][j].pb(mkp(u,v));
        }
    }
    rep(d,1,k) rep(i,1,d) 
    {
        len[d][i]=edge[d][i].size();
        rep(j,0,len[d][i]-1)
        {
            int u=edge[d][i][j].fir,v=edge[d][i][j].sec;
            if ((in[d][i][v]) || (out[d][i][u])) {ban[d][i]=1;break;}
            in[d][i][v]=1;out[d][i][u]=1;
        }
    }
    rep(d1,1,k) rep(i1,1,d1) 
    {
        if (ban[d1][i1]) continue;
        rep(d2,d1+1,k) rep(i2,1,d2)
        {
            if (ban[d2][i2]) continue;
            if (((in[d1][i1]&in[d2][i2]).any()) || ((out[d1][i1]&out[d2][i2]).any())) cant[d1][i1][d2][i2]=1;
        }
    }
    dfs(1,0);
    printf("%d",ans);
    return 0;
}

C

两个串相似等价于两个串中B和N的个数是相同的,对某个给定的字符串,用一个二元组\((x,y)\)来刻画其中B和N的个数,注意到\(x>0,y>0\)时串中一定存在BN或NB,那么\((x_1,y_1)\to (x_2,y_2)\)的最少操作次数如下:

\[\begin{cases} \max(|x_1-x_2|,|y_1-y_2|) & \mathrm{sgn}(x1-x2)=\mathrm{sgn}(y_1-y_2)\\ |x_1-x_2|+|y_1-y_2|& \mathrm{sgn}(x1-x2)\neq \mathrm{sgn}(y_1-y_2) \end{cases} \]

其中\(\mathrm{sgn}(x)\)表示x的正负性。

考虑原问题,一个显然的想法是二分一个最大的操作次数mid再判断是否存在一个满足条件的串。check时首先对每个\(x_i,y_i\)都有一个变化区间,如果最后\(\mathrm{sgn}|x'-x|=\mathrm{sgn}|y'-y|\),那么\(x'\)\(y'\)的差也会有一个区间的限制。对所有同类的区间求交后可以枚举\(x‘\)的取值,根据y的取值区间和差的取值区间找寻是否有合法的y即可。

int n,x[N],y[N];
char s[N];

int chk(int mid)
{
    int xl=0,xr=1e9,yl=0,yr=1e9,mnd=-1e9,mxd=1e9;
    rep(i,1,n)
    {
        xl=max(xl,x[i]-mid);xr=min(xr,x[i]+mid);
        yl=max(yl,y[i]-mid);yr=min(yr,y[i]+mid);
        mnd=max(mnd,y[i]-x[i]-mid);
        mxd=min(mxd,y[i]-x[i]+mid);
    }
    rep(i,xl,xr)
    {
        int l=max(yl,i+mnd),r=min(yr,i+mxd);
        l=max(0,l);
        if ((!i) && (!r)) continue;
        if (l<=r) return 1;
    }
    return 0;
}

void chk2(int mid)
{
    int xl=0,xr=1e9,yl=0,yr=1e9,mnd=-1e9,mxd=1e9;
    rep(i,1,n)
    {
        xl=max(xl,x[i]-mid);xr=min(xr,x[i]+mid);
        yl=max(yl,y[i]-mid);yr=min(yr,y[i]+mid);
        mnd=max(mnd,y[i]-x[i]-mid);
        mxd=min(mxd,y[i]-x[i]+mid);
    }
    rep(i,xl,xr)
    {
        int l=max(yl,i+mnd),r=min(yr,i+mxd);
        l=max(0,l);
        if ((!i) && (!r)) continue;
        if (l<=r) 
        {
            rep(j,1,i) putchar('B');
            rep(j,1,r) putchar('N');
            return;
        }
    }
}

int main()
{
    n=read();
    rep(i,1,n)
    {
        scanf("%s",s+1);
        int m=strlen(s+1);
        rep(j,1,m)
        {
            if (s[j]=='B') x[i]++;else y[i]++;
        }
    }
    int l=0,r=1e9,ans=0;
    while (l<=r)
    {
        int mid=(l+r)>>1;
        if (chk(mid)) {ans=mid;r=mid-1;}
        else l=mid+1;
    }
    printf("%d\n",ans);
    chk2(ans);
    return 0;
}

D

\(f_{u,0/1}\)表示已处理完子树\(u\)的所有边,且\(u\to fa_u\)的边是向下/上的最小的t之和。注意到子树的根\(u\)的贡献只与状态的第二维和它儿子中向上走的个数有关,故在当前状态合法时,可以先强制所有边均是从u向下走的,再枚举有几个儿子为向上走(这一部分的\(f_v\)的贡献可以由贪心算出),再尽可能的合并路径后算出\(t_u\)的贡献。

struct node{int to,nxt;}sq[N<<1];
int all=0,head[N];
void addedge(int u,int v)
{
    all++;sq[all].to=v;sq[all].nxt=head[u];head[u]=all;
}

int n,h[N],t[N];
ll f[N][2];

void dfs(int u,int fu)
{
    vl g;g.clear();ll sum=0;
    go(u,i)
    {
        if (v==fu) continue;
        dfs(v,u);
        sum+=f[v][0];
        g.pb(f[v][1]-f[v][0]);
    }
    sort(g.begin(),g.end());
    int m=g.size();ll tmp=sum;
    if ((!fu) || (h[u]<=h[fu]))
    {
        f[u][0]=tmp+1ll*t[u]*max(m,1);
        rep(i,1,m)
        {
            tmp+=g[i-1];
            f[u][0]=min(f[u][0],tmp+1ll*t[u]*max(m-i,i+(fu>0)));
        }
    }
    else f[u][0]=1e12;
    tmp=sum;
    if ((!fu) || (h[u]>=h[fu]))
    {
        f[u][1]=tmp+1ll*t[u]*(m+(fu>0));
        rep(i,1,m)
        {
            tmp+=g[i-1];
            f[u][1]=min(f[u][1],tmp+1ll*t[u]*max(m-i+(fu>0),i));
        }
    }
    else f[u][1]=1e12;
}

int main()
{
    n=read();
    rep(i,1,n) t[i]=read();
    rep(i,1,n) h[i]=read();
    rep(i,1,n-1)
    {
        int u=read(),v=read();
        addedge(u,v);addedge(v,u);
    }
    dfs(1,0);
    printf("%lld",min(f[1][0],f[1][1]));
    return 0;
}
posted @ 2020-08-13 13:03  EncodeTalker  阅读(230)  评论(0编辑  收藏  举报