D
一个 节点的树,两人轮流选择一条树上的边并断开,删除不包含 号点的连通块,当一人不能操作时输,求胜者
树上 游戏,每个节点的 值是所有儿子 值 的异或和,判断根节点 函数值是否为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const int N=1e5+100; int n; vector<int> G[N];
int dfs(int x,int fa=0){ rei res=0; for(int y:G[x]) if(y!=fa) res^=dfs(y,x)+1; return res; }
int main(){ scanf("%d",&n); for(rei i=1,u,v;i<n;++i) scanf("%d%d",&u,&v),G[u].push_back(v),G[v].push_back(u); puts(dfs(1) ? "Alice" : "Bob"); getchar(),getchar(); return 0; }
|
E
有 块不规则拼图,每块拼图看成连接在一起的三个宽度为 的矩形,如图
具体地,其中中间的矩形的高度为 ,左侧矩形的高度为 ,距离中间矩形底部 ,右侧矩形的高度为 ,距离中间矩形底部
将这些拼图放入一个边长为
的正方形中,需要满足如下条件:
对于一块拼图,如果其一侧的小矩形不接地,那么这个小矩形一定与另一拼图接地的小矩形相接,即,其高度无关紧要,只关心非 “接地” 小矩形的
而对于接地的小矩形有 ,此时需要关注高度
于是,对于每块拼图的一侧,我们可以给它对应到个参数:如果是接地的,称它是 或 ,否则,称它为 或
显然有: 的一侧与某矩形 相接,且最两端矩形的两侧是
考虑到 相连没有什么好性质,不妨利用左右矩形:对于左侧小矩形,点 表示 ,点 表示 ;右侧矩形则相反,点 表示 ,点 表示
两个拼图的连接处就是相同的点:全为 或全为
那么,对于拼图 连接 ,那么一连串拼图构成的组对应所得的图上的一条有向边,且路径起点为 ,终点为
那么转化为判断整张图是否能被拆分成若干个从 连向 的有向路径即可
考虑建立超级点 ,能向所有形如 的点不断提供入边,也能使足够多的 连向它
那么对于路径 ,在两侧分别补上 和 ,如此得到一个有向圈
那么在连接若干条与 的边后,图 将变为 图
考虑到 图的性质:
由此:在删去 及其关联的边后, ,
首先,如果一张图满足上述条件,那么我们将 点与若干个点连边后,可以得到一张 图 (且这个方案事实上是唯一的,即我们可以算出 该向每个点连多少条边)。
其次,由 图的性质,可得到 的若干条 回路 (这里用若干的原因是 不一定连通)。
那么,考虑其中的每个圈 (注意与回路的区别):
如果它经过 ,那么将 点去掉后,就得到一个可行的路径。
如果这个圈不经过点
那么,如果这个圈可以和一个包含 点的圈合并,得到一个大的回路,那么这个回路也满足条件,即,边不重复。于是,希望这些圈尽可能地进行合并
所以,考虑最终得到的每个图的连通分量 ( 图的强连通性保证这里的强连通分量和弱连通分量是同一个),如果存在一个不包含 的连通分量,则问题是无解的 —— 因为这个圈/回路不包含 ,从而不可能找到一条对应的路径经过 和圈/回路中所有的边。
而反之,如果所有连通分量都包含 (从而只有一个连通分量),那么问题就是有解的了。
于是,我们使用并查集维护一下连通性就可以了。注意到,在满足性质的条件下,一个点和 相连当且仅当它的入度和出度不相等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| const int N=410; int n,H,V; int fa[N],in[N],out[N]; bool dicyc[N];
int get_anc(int x){return fa[x]==x ? x : fa[x]=get_anc(fa[x]);}
int main() { int Lu,Ld,Ru,Rd,L,R; scanf("%d%d",&n,&H); V=H<<1; iota(fa,fa+V,0); for(rei i=1;i<=n;++i){ scanf("%d%d%d%d",&Lu,&Ru,&Ld,&Rd); ++out[ L=(Ld ? Ld+H : Lu)-1 ]; ++in[ R=(Rd ? Rd : Ru+H)-1 ]; fa[ get_anc(L) ]=get_anc(R); } rei i; for(i=0;i<H && in[i]<=out[i] && in[i+H]>=out[i+H];++i); if(i!=H) return puts("NO"),0; for(rei i=0;i<V;++i) dicyc[ get_anc(i) ] |= in[i]!=out[i] || !in[i]; for(i=0;i<V && (fa[i]!=i || dicyc[i]);++i); puts(i==V ? "YES" : "NO"); getchar(),getchar(); return 0; }
|
F
你有一个由 个点组成的等边三角形。 在 点左下的是 点, 右下的是 点. 现在在上面画 条从 开始, 结束的连续路径 , 满足对于任意 , 第 条线的任意一个部分不在第 条线的左边。

另外,还有 个限制 , 第 个限制形如 ,表示第 条路径, 在第 次决策的时候, 如果 则必须走左下, 否则必须走右下。
求出一共有多少种不同的画路径的方案满足以上的要求。
有一个朴素状压 ,用 直接枚举状态转移
而直接枚举状态显然多余考虑了很多不合法状态
有神仙思路如下:
设 为向左走, 为向右走,不考虑方向的限制,合法转移后的状态一定满足任何一个前缀 的个数都大于之前状态前缀 的个数
那么设 表示第 条路径的第 条转移,转移状态的前 位与 相同,考虑向右的情况:直接把后面的第一个 变为 ,然后提到前面,如此,中间段取 都满足要求,不会向左越过原来的,总复杂度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| const int N=21; const int mod=1e9+7; int n,m,k,all,last=1,cur,ans; int dir[N][N],dp[2][1<<N];
inline void fix(int &x){ x>=mod ? x-=mod : 0;} inline int lowbit(int x){ return x&(-x);}
int main(){ scanf("%d%d%d",&n,&m,&k);--n; all=(1<<n)-1; for(rei i=1,x,y,foo;i<=k;++i) scanf("%d%d%d",&x,&y,&foo),dir[x][y-1]=foo+1; dp[0][0]=1; for(rei i=1;i<=m;++i) for(rei j=0;j<n;++j){ swap(last,cur); rei foo=1<<j,bar=all^((foo<<1)-1); for(rei s=0;s<=all;++s) dp[cur][s]=0; for(rei s=0;s<=all;++s){ if(dp[last][s]){ if(dir[i][j]!=2 && (!(foo&s))) fix(dp[cur][s]+=dp[last][s]); if(dir[i][j]!=1){ rei x; if(foo&s) x=s; else{ if(!(bar&s)) x=s|foo; else x=(s|foo)^lowbit(bar&s); } fix(dp[cur][x]+=dp[last][s]); } } } } for(rei i=0;i<=all;++i) fix(ans+=dp[cur][i]); printf("%d\n",ans); getchar(),getchar(); return 0; }
|
Gitalking ...