题意:
m个开关,n个球。每个开关有两个状态,左或右,左则滚到左边,右则滚到右边。每滚一个球状态则反转一次。问最后每个开关的状态是怎样的。
解题:
因为球数实在太多(10^18),一个一个模拟果断不行,而开关的最后状态只取决于滚过的球数和初始状态。故我决定n个球一起传,然而,因为有些节点可能后面才扫到,而它的子节点已经把球传下去了,没考虑到这一点,wa了一次。改了之后,因为递归重复过多,TLE了。又改成队列,RE了。又优化下,从下往上算,还是RE。最后,才知道,原来可以用in数组来记录每个节点的入度,只有当该节点入度减为0后,才把该节点push到队列。
坑点:
1.L,R可以相同,处理需注意。
2.入度为0的点,不只1一个。其他节点也可以入度为0。(这是有影响的哦!)
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <queue> using namespace std; bool status[500005],LR; long long int amount[500005]; long long int n,x,y; int m,a,b,lft[500005],rght[500005],tmp,in[500005],L,R; char c; queue <int> cal; int main() { while(~scanf("%lld%d",&n,&m)) { memset(status,0,sizeof(status)); memset(amount,0,sizeof(amount)); memset(in,0,sizeof(in)); memset(lft,0,sizeof(lft)); memset(rght,0,sizeof(rght)); getchar(); amount[1]=n; for(int i=1;i<=m;i++) { scanf("%c %d %d",&c,&a,&b); getchar(); lft[i]=a; rght[i]=b; if(c=='L') status[i]=1; in[a]++; in[b]++; } for(int i=1;i<=m;i++) { if(in[i]==0) cal.push(i); } while(!cal.empty()) { tmp=cal.front(); cal.pop(); L=lft[tmp]; R=rght[tmp]; if(L||R) { x=(amount[tmp]+1)/2; y=amount[tmp]-x; if(status[tmp]) { amount[L]+=x; amount[R]+=y; } else { amount[L]+=y; amount[R]+=x; } in[L]--; in[R]--; if(L==R) { if(L&&in[L]==0) cal.push(L); } else { if(in[L]==0&&L) cal.push(L); if(in[R]==0&&R) cal.push(R); } } } for(int i=1;i<=m;i++) { if(amount[i]%2)status[i]=!status[i]; if(status[i])printf("L"); else printf("R"); } printf("\n"); } return 0; }