Codeforces Round #699 (Div. 2)

A - Space Navigation

设最终走到点 ( x , y ) (x,y) (x,y),然后,删除一个 L L L相当于 x + 1 x+1 x+1,删除一个 R R R相当于 x − 1 x-1 x1,删 U U U相当于 y − 1 y-1 y1,删 D D D相当于 y + 1 y+1 y+1
判断 ( x , y ) (x,y) (x,y)到点 ( p x , p y ) (px,py) (px,py)是否可能通过上述操作到达即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+5;
char s[N];
int main()
{
    int t;scanf("%d",&t);
    while(t--)
    {
        int l=0,r=0,u=0,d=0,x=0,y=0;
        int px,py;scanf("%d%d",&px,&py);
        scanf("%s",s+1);
        for(int i=1;s[i];i++)
        {
            if(s[i]=='L') l++,x--;
            if(s[i]=='R') r++,x++;
            if(s[i]=='U') u++,y++;
            if(s[i]=='D') d++,y--;
        }
        if(x==px&&y==py) printf("YES\n");
        else
        {
            if((x>=px&&r>=x-px||px>=x&&l>=px-x)&&(y>=py&&u>=y-py||py>=y&&d>=py-y))
                printf("YES\n");
            else printf("NO\n");
        }
    }
}

B - New Colony

从左到右枚举 h i > h i − 1 h_i>h_{i-1} hi>hi1的位置,球要扔过 h i h_i hi,就必须使得其左边的都达到高度 h i h_i hi,然后左边是一个下降的阶梯,填这个阶梯即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+5;
int n;
ll k,h[N];
int main()
{
    int t;scanf("%d",&t);
    while(t--)
    {
        scanf("%d%lld",&n,&k);
        for(int i=1;i<=n;i++)
            scanf("%lld",&h[i]);
        h[0]=99999999999ll;
        bool flag=false;
        for(int i=2;i<=n;i++)
            if(h[i]>h[i-1])
        {
            ll sum=0;
            for(int j=i-1;j>=1;j--)
                if(h[j]<=h[i])
                sum+=h[i]-h[j];
            else break;
            if(sum>=k)
            {
                flag=true;
                for(int j=i-1,len=1;j>=1;j--,len++)
                {
                    ll x=min(h[i]-h[j],h[j-1]-h[j]);
                    ll s=x*len;
                    if(s>=k)
                    {
                        k=k-k/len*len;
                        if(k==0) k=len;
                        printf("%d\n",i-k);
                        break;
                    }
                    else k-=s;
                }
                break;
            }
            else
            {
                k-=sum;
                for(int j=i-1;j>=1;j--)
                    if(h[j]<=h[i]) h[j]=h[i];
                else break;
            }
        }
        if(!flag) printf("-1\n");
    }
}

C - Fence Painting

最后一个画家的颜色没有出现过,答案为 N O NO NO
否则,没出现过颜色的画家都刷最后一个画家刷的那块。
其次,判断一下 b i ≠ a i b_i\neq a_i bi=ai的颜色是否可以被刷好。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,m,a[N],b[N],c[N],f[N],ans[N];
bool vis[N];
vector<int>v[N];
int main()
{
    int t;scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) vis[i]=false,v[i].clear();
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<=n;i++) scanf("%d",&b[i]),vis[b[i]]=true,f[b[i]]=i;
        for(int i=1;i<=m;i++) scanf("%d",&c[i]);
        if(!vis[c[m]])
        {
            printf("NO\n");continue;
        }
        int p;
        for(int i=1;i<=n;i++) if(b[i]==c[m]) p=i;
        for(int i=1;i<=n;i++)
            if(b[i]!=a[i]) v[b[i]].push_back(i);
        if(v[c[m]].size()!=0)
        {
            p=v[c[m]].back();v[c[m]].pop_back();
        }
        for(int i=1;i<=m;i++)
        {
            if(!vis[c[i]]||i==m) ans[i]=p;
            else
            {
                if(v[c[i]].size())
                {
                    ans[i]=v[c[i]].back();
                    v[c[i]].pop_back();
                }
                else ans[i]=f[c[i]];
            }
        }
        bool flag=true;
        for(int i=1;i<=n;i++) if(v[i].size()) flag=false;
        if(!flag) printf("NO\n");
        else
        {
            printf("YES\n");
            for(int i=1;i<=m;i++)
                printf(i==m?"%d\n":"%d ",ans[i]);
        }
    }
}

D - AB Graph

n n n是奇数,随便找两个点反复横跳即可。
否则,判断是否存在 i , j i,j i,j满足 s i , j ≠ s j , i s_{i,j}\neq s_{j,i} si,j=sj,i,如果有,在 i , j i,j i,j反复横跳即可。
否则,当 n = 2 n=2 n=2,可以证明没法得到长度为偶数的回文了。
再者,当 n > 2 n>2 n>2,任取三个点 a , b , c a,b,c a,b,c,如果三个点之间有环,在环上转圈圈即可。
否则,这三个点的图一定是以下形式:
在这里插入图片描述
接下来手推长度为 m m m的回文路径:
m = 2 : 1 → 2 → 3 , a a m=2:1\rightarrow 2\rightarrow 3,aa m=2123,aa
m = 4 : 2 → 3 → 2 → 1 → 2 , a b b a m=4:2\rightarrow 3\rightarrow 2\rightarrow1 \rightarrow2,abba m=423212,abba
m   m o d   4 = 0 : m\bmod 4=0: mmod4=0重复 m = 4 m=4 m=4的情况 m 4 \frac{m}{4} 4m次。
m = 3 : 2 → 3 → 1 → 2 , a b a m=3:2\rightarrow 3\rightarrow 1\rightarrow2,aba m=32312aba
m   m o d   3 = 0 : m\bmod 3=0: mmod3=0重复 m = 3 m=3 m=3的情况 m 3 \frac{m}{3} 3m次。
m   m o d   3 = 1 m\bmod 3=1 mmod3=1,注意到奇数可以随便,这里 m m m为偶数,在最中间放一个 m = 4 m=4 m=4的回文,这样 ( m − 4 )   m o d   3 = 0 (m-4)\bmod 3=0 (m4)mod3=0,两边放 m = 3 m=3 m=3的回文即可。
m   m o d   3 = 2 m\bmod 3=2 mmod3=2,两边两个 m = 4 m=4 m=4的回文,这样 ( m − 8 )   m o d   3 = 0 (m-8)\bmod 3=0 (m8)mod3=0,中间放一些 m = 3 m=3 m=3的回文即可。
这样,所有答案都构造完毕。个人认为这题比 E E E难点,毕竟手推不容易。

E - Sorting Books

对于 . . . x l . . . x m . . . x r . . . ...x_l...x_m...x_r... ...xl...xm...xr...,如果 x x x不往后扔,则一定要把它中间的全扔了。或者可以仍前面一部分的,如把 x l x_l xl扔到后面变成 . . . x m . . . x r . . . x l ...x_m...x_r...x_l ...xm...xr...xl,此时在 x r x_r xr x l x_l xl之间的都需要往后扔。
考虑哪些不往后扔,若不扔 . . . x l . . . x m . . . x r . . . ...x_l...x_m...x_r... ...xl...xm...xr...,则得到一个带权区间 [ l , r , v a l ] [l,r,val] [l,r,val] v a l val val [ l , r ] [l,r] [l,r]区间中 x x x的数量,即不扔 [ l , r ] [l,r] [l,r]之间的 x x x可以少扔 v a l val val次。如果 x , y x,y x,y都不扔,则他们的带权区间不能相交。
问题转换为给定若干个带权区间 [ l i , r i , v a l ] [l_i,r_i,val] [li,ri,val],找出一些不相交的区间,使得它们的权值和最大化。(即尽可能多的 x x x不扔)。用树状数组即可求出答案。
最后用总数减去不扔的数量。

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int n,a[N],bit[N],val[N],l[N],r[N],vis[N];
struct node
{
    int l,r,val;
    node(int l,int r,int val):l(l),r(r),val(val){}
    bool operator<(const node&o)const
    {
        if(l==o.l) return r<o.r;
        return l<o.l;
    }
};
vector<node>v;
int f[N];
void add(int x,int val)
{
    while(x<=n) bit[x]=max(bit[x],val),x+=x&-x;
}
int query(int x)
{
    int ans=0;
    while(x)
        ans=max(ans,bit[x]),x-=x&-x;
    return ans;
}
int Dp()
{
    sort(v.begin(),v.end());
    int ans=0;
    for(node x:v)
    {
        int dp=query(x.l-1)+x.val;
        add(x.r,dp);
        ans=max(ans,dp);
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),val[a[i]]++;
    for(int i=1;i<=n;i++) l[i]=n+1;
    for(int i=1;i<=n;i++) l[a[i]]=min(l[a[i]],i),r[a[i]]=max(r[a[i]],i);
    int ans=0;
    for(int i=1;i<=n;i++)
        if(r[i])
    {
        v.push_back(node(l[i],r[i],val[i]));
        ans+=val[i];
    }
    for(int i=n;i>=1;i--)
    {
        vis[a[i]]++;
        v.push_back(node(i,n,vis[a[i]]));
    }
    int dp=Dp();
    printf("%d\n",ans-dp);
}

F - AB Tree

最优的方法就是先把层数低的先涂相同的颜色,直到不能涂为止。
设一共 m m m层,第 i i i层的点数为 a i a_i ai
如果能找出一个 { 1 , 2 , . . , m } \{1,2,..,m\} {1,2,..,m}的子集 { p 1 , p 2 , . . . , p k } \{p_1,p_2,...,p_k\} {p1,p2,...,pk}使得 ( ∑ i = 1 k a p i ) = x (\sum\limits_{i=1}^ka_{p_i})=x (i=1kapi)=x,则每一层都可以涂相同的颜色,答案为 m m m,用背包和 b i t s e t bitset bitset优化即可求出这个子集。
否则,最多只有一层的颜色需要被染成两种颜色,找出叶子结点最多的一层,把不同的颜色全部染到这层的叶子结点上,这样答案只会增加 1 1 1,答案为 m + 1 m+1 m+1,显然这是最优的答案了,同样,找出这一层后也可以用背包和 b i t s e t bitset bitset优化找出子集。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m,x,y,tot,dep[N],fa[N],a[N],head[N],nex[N<<1],to[N<<1];
bool use[N],lev[N];
int clev[N];
void add(int u,int v)
{
    to[++tot]=v;nex[tot]=head[u];head[u]=tot;
}
void dfs(int u)
{
    lev[u]=true;
    dep[u]=dep[fa[u]]+1;
    a[dep[u]]++;
    m=max(m,dep[u]);
    for(int i=head[u];i;i=nex[i])
        dfs(to[i]),lev[u]=false;
    if(lev[u]) clev[dep[u]]++;
}
bitset<50001>b;
bitset<50001>c[20001],g[10];
int main()
{
    scanf("%d%d",&n,&x);y=n-x;
    for(int i=2;i<=n;i++)
    {
        scanf("%d",&fa[i]);
        add(fa[i],i);
    }
    dfs(1);
    vector<pair<int,int>>v;
    for(int i=1;i<=m;i+=20000)
        v.push_back({i,min(i+20000-1,m)});
    b[0]=g[0][0]=1;
    for(int i=1,j=0;i<=m;i++)
    {
        b|=b<<a[i];
        if(i==v[j].second)
        {
            j++;
            g[j]=b;
        }
    }
    if(b[min(x,y)])
    {
        printf("%d\n",m);
        int t=x,s=1;
        if(y<x) t=y,s^=1;
        for(int i=v.size();i>=1;i--)
        {
            int l=v[i-1].first,r=v[i-1].second;
            c[0]=g[i-1];
            for(int j=l;j<=r;j++)
                c[j-l+1]=c[j-l]|(c[j-l]<<a[j]);
            for(int j=r;j>=l;j--)
                if(t>=a[j]&&c[j-l][t-a[j]])
                t-=a[j],use[j]=s;
                else use[j]=s^1;
        }
        assert(t==0);
        for(int i=1;i<=n;i++)
            if(use[dep[i]]) putchar('a');
            else putchar('b');
    }
    else
    {
        printf("%d\n",m+1);
        int mx=-1,p=-1;
        for(int i=1;i<=m;i++)
            if(clev[i]>mx)
            mx=clev[i],p=i;
        assert(p!=-1);
        a[p]-=mx;
        b.reset();
        b[0]=1;
        for(int i=1,j=0;i<=m;i++)
        {
            b|=b<<a[i];
            if(i==v[j].second)
            {
                j++;
                g[j]=b;
            }
        }
        int t=x,s=1;
        if(y<x) t=y,s^=1;
        bool flag=false;
        for(int i=t;i>=0;i--)
            if(b[i]&&n-mx-i<=n-t)
        {
            flag=true;t=i;break;
        }
        assert(flag);
        for(int i=v.size();i>=1;i--)
        {
            int l=v[i-1].first,r=v[i-1].second;
            c[0]=g[i-1];
            for(int j=l;j<=r;j++)
                c[j-l+1]=c[j-l]|(c[j-l]<<a[j]);
            for(int j=r;j>=l;j--)
                if(t>=a[j]&&c[j-l][t-a[j]])
                t-=a[j],use[j]=s;
                else use[j]=s^1;
        }
        assert(t==0);
        for(int i=1;i<=m;i++)
            if(use[i]) x-=a[i];
        else y-=a[i];
        assert(x>=0);assert(y>=0);assert(x+y==mx);
        for(int i=1;i<=n;i++)
            if(lev[i]&&dep[i]==p)
        {
            clev[p]--;
            if(x) putchar('a'),x--;
            else
            {
                assert(y>=0);
                putchar('b'),y--;
            }
        }
        else if(use[dep[i]]) putchar('a');
        else putchar('b');
        assert(clev[p]==0);
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值