zoukankan      html  css  js  c++  java
  • AtCoder "Grand Contest 041" E


    Grand Contest 041 - E - Balancing Network


    题意

    给定(n)条线以及(m)个平衡器,每条线自上向下按顺序排列,每个平衡器自左向右按顺序排列,并连接着两条线(x,y)

    每条线最左边在刚开始都各有一个标记,每个标记都会向右走

    对于每个标记,如果某时刻在第(i)条线,且该位置有个平衡器由第(i)条线指向第(j)条线,那么这个标记将会走到第(j)条线并继续向右走

    相反,如果这个平衡器由其他线指向第(i)条线,则令牌不会受到这个平衡器的影响

    试对两种子问题((T))的不同条件来为每个平衡器确定一个方向(指向下或者指向上)

    子问题一:最终所有标记走到一条线上

    子问题二:最终所有标记不全在一条线上


    限制

    (2leq nleq 50000)

    (1leq mleq 100000)

    (1leq Tleq 2)

    (1leq x_ilt y_ileq N)




    思路

    对于子问题一(最终所有标记走到一条线上)

    首先我们需要知道初始时每条线上的标记最终可能走到的线是哪些,所以可以考虑逆序遍历所有平衡器,对于每个标记以 bitset<50010> 存储是否能走到某条线

    初始时,设置 (bitset[i][i]=1) ,表示如果所有连接着第(i)条线的平衡器都是指向第(i)条线,最终第(i)条线上的标记还是会在第(i)条线上

    逆序(从右向左)遍历平衡器,对于一个连接(i,j)的平衡器,发现

    • 如果让这个平衡器由(i)指向(j),那么(i)线上的标记就可以走到(j)线

    • 相反,(j)线上的标记就可以走到(i)线

    所以考虑取或运算,以 bitset[i]|bitset[j]​ 作为两条线上的标记的状态即可

    全部遍历完后,对所有(n)条线上的标记进行一次取与运算

    对于最终得到的 (bitset) ,其中为(1)的位置就是所有标记可以共同走到的线

    如果这个 (bitset) 全为0,说明无解

    所以对最后这个 (bitset) 每一位再进行遍历,一旦发现某一位(第(i)位)为(1),则设置第(i)条线作为终点

    设置(vis)数组进行访问标记,初始设置vis[i]=true,表示第(i)位此时可用

    接下来重新逆序(从右向左)遍历平衡器,对于所有平衡器,让其指向此时已经标记过的点即可(类似于BFS)

    例如对于连接(x,y)的平衡器,此时vis[x]=truevis[y]=false,说明逆序推导的过程中还没考虑到第(y)根线上的标记,所以我们只需要将第(y)根线上的标记引导至已经考虑到的第(x)根线上即可,指向就是(x ightarrow y)

    遍历完后所有平衡器就确定了状态,输出即可


    对于子问题二(最终所有标记不全在一条线上)

    首先由于(mgeq 1),所以(n=2)时标记最终一定会走到同一条线上,无解

    此时可以证明总存在一种构造方案,使得(n=3)时一定有解

    对于(n>3)的情况,由于给定的(x<y),只需要让所有(y>3)的平衡器全部设置成(y ightarrow x)即可,最终总能回到(n=3)的情况

    考虑(n=3)时的构造方法,同样,逆序(从右向左)遍历平衡器

    假设此时从第(m)个平衡器遍历到了第(k)个平衡器

    (pos[i])来表示只考虑(k)(n)这些平衡器时,原本在第(i)根线上的标记最后位于哪条线上

    (num[i])来表示只考虑(k)(n)这些平衡器时,最后第(i)根线上会有几个标记存在

    那么我们的遍历也就是每次在最前面多考虑一个平衡器,并且由题意可知,这个平衡器的优先级会更高

    考虑此时遍历到的连接(x)(y)的平衡器(k)

    如果经过(k+1)(n)这些平衡器后,(x)根线上存在的标记数量比第(y)根线上存在的标记数量更多,由于第(k)个平衡器优先级更高,那我们就可以从第(x)根线上占用一个标记到第(y)根线

    此时num[x]--,num[y]++,pos[x]=pos[y]

    反之,就可以从第(y)根线上占用一个标记到第(x)根线

    考虑到(n=3),所以让某个较大的数(-1),让某个较小的数(+1),可以保证不会有某个数达到(3),故肯定存在着方案能够满足题意




    程序

    #include<bits/stdc++.h>
    using namespace std;
    
    int n,m,T;
    int x[100050],y[100050];
    bitset<50010> bs[50050],tmp;
    bool vis[100050];
    char ans[100050];
    
    void solve1()
    {
        for(int i=1;i<=n;i++)
            bs[i][i]=1;
        for(int i=m;i;i--) //对于每个平衡器连接的两条线,标记其能走到的位置
            bs[x[i]]=bs[y[i]]=bs[x[i]]|bs[y[i]];
        tmp.set(); //全置1
        for(int i=1;i<=n;i++)
            tmp&=bs[i];
        for(int i=1;i<=n;i++) //枚举最终得到的bitset的每一位
        {
            if(tmp[i]) //如果该位值为1
            {
                vis[i]=true; //标记第i条线为最终到达的线
                for(int j=m;j;j--)
                {
                    if(vis[y[j]]) //如果y已经被访问到,则x指向y
                    {
                        vis[x[j]]=true;
                        ans[j]='v';
                    }
                    else
                    {
                        ans[j]='^'; //否则直接y指向x即可
                        if(vis[x[j]])
                            vis[y[j]]=true;
                    }
                }
                ans[m+1]='';
                puts(ans+1);
                return;
            }
        }
        puts("-1"); //最终的bitset全为0时
    }
    
    void solve2()
    {
        if(n==2) //由于m>=1,所以两条线的标记最终一定会走到同一条线上,无解
        {
            puts("-1");
            return;
        }
        int pos[4]={0,1,2,3},num[4]={0,1,1,1};
        for(int i=m;i;i--)
        {
            if(y[i]>3) //对于y>3的线,直接设置y->x即可
            {
                ans[i]='^';
                continue;
            }
            if(num[pos[x[i]]]>num[pos[y[i]]]) //如果x线上的标记数量比较多
            {
                num[pos[x[i]]]--; //从x线上占用一个
                num[pos[y[i]]]++;
                pos[x[i]]=pos[y[i]]; //此时占用过来的标记会到从y线走最终能到的线上
                ans[i]='v';
            }
            else
            {
                num[pos[x[i]]]++;
                num[pos[y[i]]]--;
                pos[y[i]]=pos[x[i]];
                ans[i]='^';
            }
        }
        ans[m+1]='';
        puts(ans+1);
    }
    
    int main()
    {
        scanf("%d%d%d",&n,&m,&T);
        for(int i=1;i<=m;i++)
            scanf("%d%d",&x[i],&y[i]);
        if(T==1)
            solve1();
        else
            solve2();
        return 0;
    }
    

  • 相关阅读:
    jsp表单数据添加到数据库
    javaweb 复习随笔
    [组 原]
    [组 原]
    均方差、交叉熵及公式推导
    网络安全知识网站
    docker搭建渗透环境并进行渗透测试
    SQL注入之-DECLARE时间盲注
    Apache Flink Dashboard未授权访问导致任意Jar包上传漏洞
    Dnscat2实现DNS隐蔽隧道反弹Shell
  • 原文地址:https://www.cnblogs.com/stelayuri/p/13779693.html
Copyright © 2011-2022 走看看