多校第十五场 题解
Posted skyh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多校第十五场 题解相关的知识,希望对你有一定的参考价值。
A. zsy家今天的饭
对于 (inom{m}{k}) 种方案,答案是跨过的边权*2-直径。
可以对两部分分别计算贡献。
对于前者,可以考虑计算每条边的贡献。
若将餐厅点集划分为 (a,b) 两部分,那么乘上的系数就是 (inom{m}{k}-inom{a}{k}-inom{b}{k})。
对于后者,可以考虑枚举直径的两个端点是谁。
有个结论是,树上到达每个点最远的点,一定在直径的端点上。
所以对于其它点,只要判断与当前枚举端点距离即可。
有个问题是如何处理直径长度相同,通过讨论可以发现只要比较标号就是正确的。
?
B. 划愤
考虑 (n=2) 的情况,其实和 (nim) 积的式子是一样的。
对于 (n) 比较大的情况,也就是把所有的单点 (sg) 值积在一起。
发现原问题的构造比较奇怪,其实和行列式有点类似。
然而行列式带了一个 (-1) 的系数,然而可以发现异或意义下的 (-1) 其实就是不变。
所以只要求行列式就好了。
然而并不会 (nim) 积,所以学习了一下。
大概就是解决这样一个问题: (sg(x,y)= ext{mex}_{i<x or j<y}(sg(i,j)))。
然后可以发现这样一些性质:
1.对于 (y=2^{2^x},z < y),有 (y otimes z=yz)。
2.对于 (y=2^{2^x}),有 (y otimes y=frac{3}{2}y)
3.(otimes) 满足交换律、结合律。
4.(0 otimes x=0,1 otimes x=x)
这样的话,要求 (x otimes y),可以先找出最大的 (p=2^{2^x},p leq max{x,y})。
设 (x=ap+b,y=cp+d),就有 (xotimes y=(a otimes p oplus b)otimes (c otimes p oplus d)),下面用乘法和加法代替 (nim) 积和异或。
容易发现,这样的做法会使四个子问题 (ac,bd,(a+c)(b+d),acfrac{1}{2}p) 规模都达到开根的效果。
这样递归的层数就是 (log log V),所以复杂度是 (4^{log log V}=(2^{log log V})^2=log^2V)。
可以预处理 (x,y<256),就跑的很快了。
然后为了能够进行高斯消元,需要处理逆元。
做法大概是,找到任意一个大于 (y) 的 (2^{2^x}),那么 (y^{-1}=y^{2^{2^x}-2})。
大概的理解就是,这个玩意在 (mod 2^{2^x}) 意义下是封闭的,所以满足费马小定理的样子。
#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
int n;
ull ret[257][257],A[155][155];
inline ull mul(ull x,ull y,int len=32){
if(x<=1||y<=1) return x*y;
if(len<=4&&ret[x][y]) return ret[x][y];
ull a=x>>len,b=x^(a<<len),c=y>>len,d=y^(c<<len),A=mul(a,c,len>>1),B=mul(b,d,len>>1),C=mul(a^b,c^d,len>>1),D=mul(A,1ull<<len-1,len>>1),r=D^B^((C^B)<<len);
return len<=4?ret[x][y]=r:r;
}
inline ull getinv(ull x,ull y=2,ull r=1){
while(y&&y<=x) y*=y;
for(y-=2;y;y>>=1,x=mul(x,x)) if(y&1) r=mul(r,x);
return r;
}
inline bool gauss(){
for(int i=1,j;i<=n;++i){
for(j=i;j<=n&&!A[j][i];++j); if(j>n) return 0;
swap(A[i],A[j]); ull c=getinv(A[i][i]);
for(j=i;j<=n;++j) A[i][j]=mul(A[i][j],c);
for(j=i+1;j<=n;++j) if(A[j][i]) for(int k=n;k>=i;--k) A[j][k]^=mul(A[j][i],A[i][k]);
}
return 1;
}
int main(){
freopen("sui.in","r",stdin);
freopen("sui.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) scanf("%llu",&A[i][j]);
return puts(gauss()?"xiaoDyingle":"xiaoDwandanle"),0;
}
?
C. 树上的鼠
有个结论是,先手必败与 (1) 号点在直径的中点等价。
原因大概是这样的,(1) 号点在中点的话,就可以以 (1) 号点为根建树出来。
对于先手的每个操作,后手都可以移动到不同子树的相同深度的点,所以先手必败。
如果 (1) 号点不在中点,那么先手可以直接移动到中点,即先后手互换,所以这个结论是正确的。
所以可以直接用 (dp) 来统计这个方案数。
显然只关注子树中每个最大深度的联通块个数,这个玩意一看就很长链剖分。
所以写个长链剖分,对于小于短链长度的部分暴力合并,大于的部分打个后缀乘法标记就行了。
以上是关于多校第十五场 题解的主要内容,如果未能解决你的问题,请参考以下文章
hdu多校第五场1007 (hdu6630) permutation 2 dp
hdu多校第五场1004 (hdu6627) equation 1 计算几何
[2019杭电多校第五场][hdu6629]string matching
杭电2018多校第五场(2018 Multi-University Training Contest 5) 1007.Glad You Came (HDU6356)-区间更新-线段树+剪枝