codeforces Round #310(Div.1) 题解

嘴巴选手真爽,一不用打代码二不用掉Rating三还可以打杂。。。。
感觉这套题不难,但是被出题人出瞎了。。。

555A. Case of Matryoshkas

题目大意:给定 n 个大小从1 n 的套娃,初始套成k坨,每次你可以选择两个操作:
1.选择一个不在任何其他套娃里的套娃,将里面的套娃取出来(要求原先里面有套娃)
2.选择一个不再任何其他套娃里的套娃,将一个套娃塞进去(要求原先里面没有套娃)
求将所有套娃合并的最小操作次数

如果一个套娃 x 初始在最里面,或者满足大小为1...x1的套娃都在它里面,那么这个套娃可以不用拆开
将所有需要拆开的套娃拆开,然后一一合并

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
using namespace std;
int n,m,k,cnt,ans;
int main()
{
    int i,j,x,last;
    cin>>n>>k;
    for(i=1;i<=k;i++)
    {
        bool flag=false;
        scanf("%d",&m);
        for(j=1;j<=m;j++)
        {
            scanf("%d",&x);
            if(j>1)
                ++ans;
            if(flag&&x==last+1)
                --ans;
            else
                ++cnt,flag=false;
            if(x==1)
                flag=true;
            last=x;
        }
    }
    cout<<ans+cnt-1<<endl;
    return 0;
}

555B. Case of Fugitive

题目大意:给定 n 个岛,第i个岛可以用一个区间 [li,ri] 表示,满足对于任意 1i<n ri<li+1
现在需要将每两个相邻的岛之间架一座桥,桥的长度需要在 [li+1ri,ri+1li] 之间
给定 m 块木板,问是否能完成任务

问题可以简化为给定n1个区间和 m 个点问是否存在一个匹配满足每个点都在对应区间内且所有区间都被匹配上

将区间按左端点从大到小排序,依次扫描
每次扫描到一个区间,将所有大于等于区间左端点的点都扔进set,然后取走所有小于等于区间右端点的点中最大的那个,不存在则无解

#include <set>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 200200
using namespace std;
struct Interval{
    long long l,r,id;
    bool operator < (const Interval &i) const
    {
        return l < i.l ;
    }
}intervals[M];
int n,m,ans[M];
set< pair<long long,int> > s;
pair<long long,int> points[M]; 
int main()
{
    int i,j;
    long long _l,_r,l,r;
    cin>>n>>m>>_l>>_r;n--;
    for(i=1;i<=n;i++)
    {
        scanf("%I64d%I64d",&l,&r);
        intervals[i].l=l-_r;
        intervals[i].r=r-_l;
        intervals[i].id=i;
        _l=l;_r=r;
    }
    for(i=1;i<=m;i++)
    {
        scanf("%I64d",&points[i].first);
        points[i].second=i;
    }
    sort(intervals+1,intervals+n+1);
    sort(points+1,points+m+1);
    for(j=m,i=n;i;i--)
    {
        for(;j&&points[j].first>=intervals[i].l;j--)
            s.insert(points[j]);
        set< pair<long long,int> >::iterator it=s.upper_bound(make_pair(intervals[i].r+1,0));
        if(it==s.begin())
            return puts("No"),0;
        --it;ans[intervals[i].id]=it->second;s.erase(it);
    }
    puts("Yes");
    for(i=1;i<=n;i++)
        printf("%d%c",ans[i],i==n?'\n':' ');
    return 0;
}

555C. Case of Chocolate

题目大意:给定一个nn的巧克力,初始啃掉副对角线右下方的区域,然后每次选择副对角线上的一个位置,然后选择一个方向(上/左),一路啃下去,直到啃到一个空的位置为止,输出啃掉的长度

每啃一次会把当前啃到的块分成至多两小块
当前剩下的每一块巧克力可以用一个四元组 (l,r,left_bound,up_bound) 表示,其中 l r表示副对角线上的区间, left_bound 表示左边界, up_bound 表示上边界
每次啃的时候去set上找啃到哪块即可

#include <set>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
struct abcd{
    int l,r;
    int left_bound,up_bound;
    abcd() {}
    abcd(int _,int __,int ___,int ____):
        l(_),r(__),left_bound(___),up_bound(____) {}
    bool operator < (const abcd &a) const
    {
        return r < a.r ;
    }
};
int n,q;
set<int> v;
set<abcd> s;
int main()
{
    int i,x,y;
    char p[10];
    cin>>n>>q;
    s.insert(abcd(1,n,1,1));
    for(i=1;i<=q;i++)
    {
        scanf("%d%d%s",&y,&x,p);
        if(v.find(y)!=v.end())
        {
            puts("0");
            continue;
        }
        v.insert(y);
        set<abcd>::iterator it=s.lower_bound(abcd(0,y,0,0));
        abcd temp=*it;s.erase(it);
        if(p[0]=='U')
        {
            printf("%d\n",x-temp.up_bound+1);
            s.insert(abcd(temp.l,y-1,temp.left_bound,temp.up_bound));
            s.insert(abcd(y+1,temp.r,y+1,temp.up_bound));
        }
        else
        {
            printf("%d\n",y-temp.left_bound+1);
            s.insert(abcd(temp.l,y-1,temp.left_bound,x+1));
            s.insert(abcd(y+1,temp.r,temp.left_bound,temp.up_bound));
        }
    }
    return 0;
}

555D. Case of a Top Secret

题目大意:给定 n 个挂点,m个重锤,第 i 个重锤初始挂在第ai个挂点上,挂绳长度为 leni ,初始向右摆动,刮到一个重锤后会折返,问最后挂在哪个重锤上

每次二分找位置,记录路径,如果是 AAA 就输出答案,如果是 ABA 就取模,这样不超过 log2len 次挂绳的长度就会变为0
复杂度 O(mlog2nlog2len)
注意初始给出的挂点不一定按顺序,注意挂点的标号不是排序后的标号。。。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 200200
using namespace std;

int n,m;
int a[M],b[M],id[M];
//id[i]=j表示排序后的第i个是排序前的第j个

int stack[1001001],top;

int main()
{
    int i,pos,len;
    cin>>n>>m;
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    memcpy(b+1,a+1,sizeof(a[0])*n);
    sort(a+1,a+n+1);
    for(i=1;i<=n;i++)
        id[lower_bound(a+1,a+n+1,b[i])-a]=i;

    for(i=1;i<=m;i++)
    {
        scanf("%d%d",&pos,&len);
        pos=lower_bound(a+1,a+n+1,b[pos])-a;
        stack[top=1]=pos;
        while(1)
        {
            if( top>=3 && stack[top]==stack[top-1] && stack[top]==stack[top-2] )
            {
                printf("%d\n",id[stack[top]]);
                break;
            }
            if( top>=3 && stack[top]==stack[top-2] )
                len%=abs(a[stack[top]]-a[stack[top-1]])<<1;
            int next_pos;
            if(top&1)
                next_pos=upper_bound(a+1,a+n+1,a[pos]+len)-a-1;
            else
                next_pos=lower_bound(a+1,a+n+1,a[pos]-len)-a;
            len-=abs(a[pos]-a[next_pos]);
            pos=next_pos;stack[++top]=pos;
        }
    }
    return 0;
}

555E. Case of Computer Network

题目大意:给定一张无向图,要求有向化,使得有向化之后 si 可以到达 ti

一个边双有向化之后可以变成一个强连通分量
缩边双后剩下一座森林,每条边只能被指定一个方向,DFS一遍判断即可

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 200200
using namespace std;

struct edge{
    int x,y;
}edges[M];
struct abcd{
    int to,next;
}table[M<<1];
int head[M],tot=1;

int n,m,q,cnt;

int belong[M],dpt[M],fa[M][18];

int mark1[M],mark2[M];

void Add(int x,int y)
{
    table[++tot].to=y;
    table[tot].next=head[x];
    head[x]=tot;
}
void Tarjan(int x,int from)
{
    static int dpt[M],low[M],T;
    static int stack[M],top;
    int i;
    dpt[x]=low[x]=++T;
    stack[++top]=x;
    for(i=head[x];i;i=table[i].next)
        if(i^from^1)
        {
            if(dpt[table[i].to])
                low[x]=min(low[x],dpt[table[i].to]);
            else
                Tarjan(table[i].to,i),low[x]=min(low[x],low[table[i].to]);
        }
    if(dpt[x]==low[x])
    {
        int t;++cnt;
        do{
            t=stack[top--];
            belong[t]=cnt;
        }while(t!=x);
    }
}
namespace Union_Find_Set{
    int fa[M],rank[M];
    int Find(int x)
    {
        if(!fa[x]||fa[x]==x)
            return fa[x]=x;
        return fa[x]=Find(fa[x]);
    }
    void Union(int x,int y)
    {
        x=Find(x);y=Find(y);
        if(x==y) return ;
        if(rank[x]>rank[y])
            swap(x,y);
        if(rank[x]==rank[y])
            ++rank[y];
        fa[x]=y;
    }
}
void DFS1(int x)
{
    int i;
    dpt[x]=dpt[fa[x][0]]+1;
    for(i=1;i<=17;i++)
        fa[x][i]=fa[fa[x][i-1]][i-1];
    for(i=head[x];i;i=table[i].next)
        if(table[i].to!=fa[x][0])
        {
            fa[table[i].to][0]=x;
            DFS1(table[i].to);
        }
}
int LCA(int x,int y)
{
    int j;
    if(dpt[x]<dpt[y])
        swap(x,y);
    for(j=17;~j;j--)
        if(dpt[fa[x][j]]>=dpt[y])
            x=fa[x][j];
    if(x==y) return x;
    for(j=17;~j;j--)
        if(fa[x][j]!=fa[y][j])
            x=fa[x][j],y=fa[y][j];
    return fa[x][0];
}
void DFS2(int x)
{
    int i;
    for(i=head[x];i;i=table[i].next)
        if(table[i].to!=fa[x][0])
        {
            DFS2(table[i].to);
            mark1[x]+=mark1[table[i].to];
            mark2[x]+=mark2[table[i].to];
        }
    if(mark1[x]&&mark2[x])
        exit((puts("No"),0));
}
int main()
{
    using namespace Union_Find_Set;
    int i,x,y;
    cin>>n>>m>>q;
    for(i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        edges[i].x=x;
        edges[i].y=y;
        Add(x,y);Add(y,x);
    }
    for(i=1;i<=n;i++)
        if(!belong[i])
            Tarjan(i,0);
    memset(head,0,sizeof head);tot=0;
    for(i=1;i<=m;i++)
    {
        x=belong[edges[i].x];
        y=belong[edges[i].y];
        if(x!=y)
            Add(x,y),Add(y,x),Union(x,y);
    }
    for(i=1;i<=cnt;i++)
        if(i==Find(i))
            DFS1(i);
    for(i=1;i<=q;i++)
    {
        scanf("%d%d",&x,&y);
        x=belong[x];
        y=belong[y];
        if(Find(x)!=Find(y))
            return puts("No"),0;
        int lca=LCA(x,y);
        mark1[x]++;mark2[y]++;
        mark1[lca]--;mark2[lca]--;
    }
    for(i=1;i<=n;i++)
        if(i==Find(i))
            DFS2(i);
    puts("Yes");
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值