BZOJ4911: [Sdoi2017]切树游戏
BZOJ 4911 切树游戏
重构了三次.jpg
每次都把这个问题想简单了.jpg
果然我还是太菜了.jpg
这种题的题解可以一眼秒掉了,FWT+动态DP简直是裸的一批...
那么接下来,考虑如何维护信息。
每个点维护$4$个信息,分别表示,这条链自底向上,自上向底,两端都在这条链的轻儿子里,和两端为链头的方案数。
这样的话,正常询问就没啥问题了,只需要每次修改和初始化的时候FWT一下,然后最后FWT回来即可。
然后这样做的话,因为FWT没有可减性(没法求逆),所以每次需要将轻儿子用线段树维护一下,然后每次重建即可
(是我菜了,FWT之间求每个对应位置的逆元然后乘起来就行...
剩下的就是DDP正常操作了...
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
#define N 30005
#define M 128
#define mod 10007
#define inv2 5004
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int n,m,Q,a[N];
struct node{int to,next;}e[N<<1];
int head[N],cnt,fa[N],anc[N],son[N],siz[N],idx[N],tims,p[N],sson[N];
// ploy
struct Ploy
{
int a[M],len;
void FWT(int opt)
{
for(int k=2,tmp;k<=m;k<<=1)
for(int i=0,t=k>>1;i<m;i+=k)
for(int j=i;j<i+t;j++)
if(opt==1)tmp=a[j],a[j]=(a[j]+a[j+t])%mod,a[j+t]=(mod+tmp-a[j+t])%mod;
else tmp=a[j],a[j]=(a[j]+a[j+t])*inv2%mod,a[j+t]=(mod+tmp-a[j+t])*inv2%mod;
}
Ploy(){}
Ploy(int x){len=1;a[0]=x;FWT(1);}
Ploy(int p,int x){memset(a,0,sizeof(a));len=m;a[p]=x;FWT(1);}
Ploy(int *b,int m){memcpy(a,b,sizeof(a));len=m;}
Ploy operator + (const Ploy &b) const
{
Ploy c;c.len=max(b.len,len);
for(int i=0;i<c.len;i++)c.a[i]=(a[i]+b.a[i])%mod;
return c;
}
Ploy operator - (const Ploy &b) const
{
Ploy c;c.len=max(b.len,len);
for(int i=0;i<c.len;i++)c.a[i]=(mod+a[i]-b.a[i])%mod;
return c;
}
Ploy operator * (const Ploy &b) const
{
Ploy c;c.len=max(b.len,len);
for(int i=0;i<c.len;i++)c.a[i]=a[i]*b.a[i]%mod;
return c;
}
int get(int x){FWT(-1);return a[x];}
void print(){FWT(-1);for(int i=0;i<m;i++)printf("%d ",a[i]);puts("");FWT(1);}
}f[N],g[N],s[N],h[N],one,tmp,A[N];
// Segment_Tree of all the sons
vector<Ploy>Tr[N];
vector<int>lsn[N];
int pos[N];
void build(int x,int l,int r,int rt)
{
if(l==r){Tr[x][rt]=f[lsn[x][l-1]]+one;pos[lsn[x][l-1]]=rt;return ;}
int m=(l+r)>>1;build(x,lson);build(x,rson);Tr[x][rt]=Tr[x][rt<<1]*Tr[x][rt<<1|1];
}
// Segment_Tree of DDP
struct Segment
{
Ploy a,b,c,d;
Segment(){}
Segment(Ploy x,Ploy y){a=b=c=x;d=x+y;}
Segment operator + (const Segment &A) const
{
Segment B;
B.a=a*A.a;
B.b=b+A.b*a;
B.c=A.c+A.a*c;
B.d=d+A.d+A.b*c;
return B;
}
}tr[N<<2];
void build(int l,int r,int rt)
{
if(l==r){tr[rt]=Segment(A[p[l]]*g[p[l]],s[p[l]]);return ;}
int m=(l+r)>>1;build(lson);build(rson);tr[rt]=tr[rt<<1]+tr[rt<<1|1];
}
Segment query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)return tr[rt];int m=(l+r)>>1;
if(R<=m)return query(L,R,lson);if(L>m)return query(L,R,rson);
return query(L,R,lson)+query(L,R,rson);
}
void Update(int x,int l,int r,int rt)
{
if(l==r){tr[rt]=Segment(A[p[l]]*g[p[l]],s[p[l]]);return ;}
int m=(l+r)>>1;if(x<=m)Update(x,lson);else Update(x,rson);tr[rt]=tr[rt<<1]+tr[rt<<1|1];
}
void Update(int x)
{
Segment tmp=Segment();
while(x)
{
Update(idx[x],1,n,1);x=anc[x];
if(fa[x])
{
tmp=query(idx[x],idx[sson[x]],1,n,1);
s[fa[x]]=s[fa[x]]-h[x]+tmp.d;f[x]=tmp.b;
h[x]=tmp.d;int rt=pos[x];Tr[fa[x]][rt]=f[x]+one;
for(rt>>=1;rt;rt>>=1)Tr[fa[x]][rt]=Tr[fa[x]][rt<<1]*Tr[fa[x]][rt<<1|1];
g[fa[x]]=Tr[fa[x]][1];
}x=fa[x];
}
}
// Graph
void add(int x,int y){e[cnt]=(node){y,head[x]};head[x]=cnt++;}
void dfs1(int x,int from)
{
fa[x]=from;siz[x]=1;f[x]=A[x];
for(int i=head[x];i!=-1;i=e[i].next)
{
int to1=e[i].to;
if(to1!=from)dfs1(to1,x),f[x]=f[x]*(f[to1]+one),h[x]=h[x]+h[to1],siz[x]+=siz[to1],siz[son[x]]<siz[to1]?son[x]=to1:0;
}h[x]=h[x]+f[x];
}
void dfs2(int x,int top)
{
anc[x]=top;idx[x]=++tims;p[tims]=x;sson[x]=x;g[x]=one;
if(son[x])dfs2(son[x],top),sson[x]=sson[son[x]];
for(int i=head[x];i!=-1;i=e[i].next)
{
int to1=e[i].to;
if(to1!=son[x]&&to1!=fa[x])dfs2(to1,to1),g[x]=g[x]*(f[to1]+one),s[x]=s[x]+h[to1],lsn[x].push_back(to1);
}
if(!lsn[x].size())return ;
Tr[x].resize(lsn[x].size()*4+5);
build(x,1,lsn[x].size(),1);
}
char opt[10];
int main()
{
int size = 0x8000000;
char*p=(char*)malloc(size) + size;
__asm__("movl %0, %%esp\n" :: "r"(p) );
scanf("%d%d",&n,&m);memset(head,-1,sizeof(head));
one.a[0]=1;one.len=1;one.FWT(1);
for(int i=1;i<=n;i++)scanf("%d",&a[i]),A[i]=Ploy(a[i],1);
for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);
dfs1(1,0);dfs2(1,1);build(1,n,1);scanf("%d",&Q);
while(Q--)
{
int x,y;scanf("%s%d",opt,&x);
if(opt[0]=='Q')tmp=query(idx[1],idx[sson[1]],1,n,1).d,printf("%d\n",tmp.get(x));
else
{
scanf("%d",&y);a[x]=y;A[x]=Ploy(a[x],1);
Update(x);
}
}
}
BZOJ4911: [Sdoi2017]切树游戏的更多相关文章
- 【BZOJ4911】[SDOI2017]切树游戏(动态dp,FWT)
[BZOJ4911][SDOI2017]切树游戏(动态dp,FWT) 题面 BZOJ 洛谷 LOJ 题解 首先考虑如何暴力\(dp\),设\(f[i][S]\)表示当前以\(i\)节点为根节点,联通子 ...
- LG3781 [SDOI2017]切树游戏
题意 题目描述 小Q是一个热爱学习的人,他经常去维基百科学习计算机科学. 就在刚才,小Q认真地学习了一系列位运算符,其中按位异或的运算符\(\oplus\)对他影响很大.按位异或的运算符是双目运算符. ...
- LOJ2269 [SDOI2017] 切树游戏 【FWT】【动态DP】【树链剖分】【线段树】
题目分析: 好题.本来是一道好的非套路题,但是不凑巧的是当年有一位国家集训队员正好介绍了这个算法. 首先考虑静态的情况.这个的DP方程非常容易写出来. 接着可以注意到对于异或结果的计数可以看成一个FW ...
- bzoj 4911: [Sdoi2017]切树游戏
考虑维护原树的lct,在上面dp,由于dp方程特殊,均为异或卷积或加法,计算中可以只使用fwt后的序列 v[w]表示联通子树的最浅点为w,且不选w的splay子树中的点 l[w]表示联通子树的最浅点在 ...
- [SDOI2017]切树游戏
题目 二轮毒瘤题啊 辣鸡洛谷竟然有卡树剖的数据 还是\(loj\)可爱 首先这道题没有带修,设\(dp_{i,j}\)表示以\(i\)为最高点的连通块有多少个异或和为\(j\),\(g_{i,j}=\ ...
- 洛谷 P3781 - [SDOI2017]切树游戏(动态 DP+FWT)
洛谷题面传送门 SDOI 2017 R2 D1 T3,nb tea %%% 讲个笑话,最近我在学动态 dp,wjz 在学 FWT,而我们刚好在同一天做到了这道题,而这道题刚好又是 FWT+动态 dp ...
- 【LOJ】#2269. 「SDOI2017」切树游戏
题解 把所有的数组一开始就FWT好然后再IFWT回去可以减小常数 从13s跑到0.7s-- 可以参照immortalCO的论文,感受一下毒瘤的动态动态DP 就是用数据结构维护线性递推的矩阵的乘积 由于 ...
- loj#2269. 「SDOI2017」切树游戏
还是loj的机子快啊... 普通的DP不难想到,设F[i][zt]为带上根玩出zt的方案数,G[i][zt]为子树中的方案数,后面是可以用FWT优化的 主要是复习了下动态DP #include< ...
- LOJ2269. 「SDOI2017」切树游戏 [FWT,动态DP]
LOJ 思路 显然是要DP的.设\(dp_{u,i}\)表示\(u\)子树内一个包含\(u\)的连通块异或出\(i\)的方案数,发现转移可以用FWT优化,写成生成函数就是这样的: \[ dp_{u}= ...
随机推荐
- docker第一章:docker核心概念及centos6下安装
Docker三大核心概念 镜像 容器 仓库 镜像 docker镜像类似于虚拟机镜像,可以将它理解为一个面向Docker引擎的只读模板,包含了文件系统. 容器 1.容器是从镜像创建的应用运行实例,容器和 ...
- Flutter 不一样的跨平台解决方案
本文主要介绍Flutter相关的东西,包括Fuchsia.Dart.Flutter特性.安装以及整体架构等内容. 1. 简介 Flutter作为谷歌最近推出的跨平台开发框架,一经推出便吸引了不少注意. ...
- iOS动画-从UIView到Core Animation
首先,介绍一下UIView相关的动画. UIView普通动画: [UIView beginAnimations: context:]; [UIView commitAnimations]; 动画属性设 ...
- (网页)Http状态码大全(200、404、500等)(转CSDN)
当我们从客户端向服务器发送请求时 服务器向我们返回状态码 状态码就是告诉我们服务器响应的状态 通过它,我们就可以知道当前请求是成功了还是出现了什么问题 状态码是由3位数字和原因短语组成的(比如最常见的 ...
- python并发编程之线程
操作系统线程理论 参考资料:http://www.cnblogs.com/Eva-J/articles/8306047.html 线程和python 理论知识 全局解释器锁GIL Python代码的执 ...
- 【hexo】03config文件配置详解
YAML 是专门用来写配置文件的语言,非常简洁和强大,我们的配置文件就是这种格式.需要了解的只有: # 我是文配置件的注释 重要提示,例如:"theme: landspace"中冒 ...
- Burp Suite 抓取http、https流量配置+CA证书安装
HTTPS协议是为了数据传输安全的需要,在HTTP原有的基础上,加入了安全套接字层SSL协议,通过CA证书来验证服务器的身份,并对通信消息进行加密.基于HTTPS协议这些特性,我们在使用Burp Pr ...
- iOS-省市区选择的实现
一.简介 该文主要实现的是省市区的选择,可在个人修改地址的地方使用. 二.需要的东西 制作这个首先需要一个area.plist文件,该文件中保存这所有的关于省市区的信息,下载地址:http://pan ...
- zk理解(转载自邬兴亮---www.cnblogs.com/wuxl360/p/5817471.html)
一.分布式协调技术 在给大家介绍ZooKeeper之前先来给大家介绍一种技术——分布式协调技术.那么什么是分布式协调技术?那么我来告诉大家,其实分布式协调技术 主要用来解决分布式环境当中多个进程之间的 ...
- Properties集合_修改配置信息
集合中的数据来自于一个文件 注意:必须要保证该文件中的数据是键值对. 需要使用到读取流 使用load()方法读取已有文件中的数据,存储到Properties集合中 public class Pro ...