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]=true
且vis[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;
}