zoukankan      html  css  js  c++  java
  • noi前第十五场 题解

    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) 积和异或。

    [egin{aligned} xy&=(ap+b)(cp+d)\ &=acp^2+bd+bcp+adp\ &=ac(p+frac{1}{2}p)+bd+(bc+ad)p\ &=acp+acfrac{1}{2}p+bd+((a+b)(c+d)-ac-bd)p\ end{aligned} ]

    容易发现,这样的做法会使四个子问题 (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) 来统计这个方案数。
    显然只关注子树中每个最大深度的联通块个数,这个玩意一看就很长链剖分。
    所以写个长链剖分,对于小于短链长度的部分暴力合并,大于的部分打个后缀乘法标记就行了。

  • 相关阅读:
    模块-和包
    re模块
    递归函数
    内置函数
    C++ 创建文件的方法
    C++多态的实现条件
    C++常见错误总结
    Http客户端跳转和服务器端跳转的区别
    struts1学习
    Java 核心读书笔记 第11章
  • 原文地址:https://www.cnblogs.com/skyh/p/13399029.html
Copyright © 2011-2022 走看看