[SDOI2017]切树游戏
二轮毒瘤题啊
辣鸡洛谷竟然有卡树剖的数据
还是\(loj\)可爱
首先这道题没有带修,设\(dp_{i,j}\)表示以\(i\)为最高点的连通块有多少个异或和为\(j\),\(g_{i,j}=\sum_{k\in Tree(i)}dp_{k,j}\) (\(k\in Tree(i)\)表示\(k\)在\(i\)子树内部)
我们可以直接把每一个权值\(fwt\)一下,大力合并就好了,合并直接对位相乘,只需要在最后\(fwt\)回来就好了
但是我们有了修改,就变成了一道非常恶心的\(ddp\)了
首先我们\(fwt\)肯定还是要\(fwt\)的,我们以下的\(dp\)都是\(fwt\)之后的
考虑我们的方程
\]
\]
我们考虑把重儿子和轻儿子分开处理,也就是\(ddp\)了
设\(f'\)表示没有处理重儿子的\(dp\)数组,\(g'\)表示没有处理重儿子的\(g\)数组
我们可以写成这样的矩阵
\]
猫老师的博客里提到这个矩阵只有一个地方是有用的,于是我们可以只存\(4\)个值来表示矩阵,从而大大优化常数
又因为我们\(fwt\)之后可以对于每一位单独考虑,于是我们直接来上\(128\)棵线段树分别维护每一位的值就好了
有一个问题就是我们需要撤回一个轻儿子的影响
设没有这个轻儿子的时候为\(f'\),轻儿子影响为\(v\)
则有
\]
则有
\]
但是如果\(1+v=0\),我们没有办法直接除掉这个影响
所以我们还需要对于每一个点维护出其有多少个轻儿子的会使得\(f\)变成\(0\),以及没有这些轻儿子的话\(f\)的值应该是多少
这样我们就能解决这个问题了
但是细节还是有一堆,非常非常难写
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const int maxn=30005;
const int mod=10007;
const int Inv=5004;
inline int read() {
char c=getchar();int x=0;while(c<'0'||x>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
struct E{int v,nxt;}e[maxn<<1];
struct mat{int a,b,c,d;};
int n,num,len,m,Q,__;
int head[maxn],son[maxn],sum[maxn],deep[maxn],fa[maxn],top[maxn],S[128],H[128],inv[mod+5];
int bot[maxn],dp[maxn][128],g[maxn][128],dfn[maxn],id[maxn],pos[maxn],val[maxn][128];
int h[maxn][128],tmp[maxn][128],p[maxn][128];
int l[maxn*3],r[maxn*3];
inline void add(int x,int y) {
e[++num].v=y;e[num].nxt=head[x];head[x]=num;
}
inline void Fwt(int *f,int o) {
for(re int i=2;i<=len;i<<=1)
for(re int ln=i>>1,l=0;l<len;l+=i)
for(re int x=l;x<l+ln;++x) {
int g=f[x],h=f[x+ln];
f[x]=(g+h)%mod,f[x+ln]=(g-h+mod)%mod;
if(o) f[x]=(f[x]*Inv)%mod,f[x+ln]=(f[x+ln]*Inv)%mod;
}
}
void dfs1(int x) {
sum[x]=1;
for(re int i=head[x];i;i=e[i].nxt) {
if(deep[e[i].v]) continue;
deep[e[i].v]=deep[x]+1,fa[e[i].v]=x;
dfs1(e[i].v);sum[x]+=sum[e[i].v];
if(sum[e[i].v]>=sum[son[x]]) son[x]=e[i].v;
}
}
int dfs2(int x,int topf) {
top[x]=topf,dfn[x]=++__,id[__]=x;
if(son[x]) dfs2(son[x],topf);
else return bot[x]=x;
for(re int i=head[x];i;i=e[i].nxt)
if(!top[e[i].v]) bot[e[i].v]=dfs2(e[i].v,e[i].v);
return bot[x]=bot[son[x]];
}
inline mat operator*(mat a,mat b) {
mat c;
c.a=a.a*b.a%mod;
c.b=(a.b+a.a*b.b%mod)%mod;
c.c=(b.c+b.a*a.c%mod)%mod;
c.d=(b.b*a.c%mod+a.d+b.d)%mod;
return c;
}
struct Segment_Tree {
mat d[maxn*3];
inline void pushup(int i) {d[i]=d[i<<1]*d[i<<1|1];}
mat query(int x,int y,int i) {
if(x<=l[i]&&y>=r[i]) return d[i];
int mid=l[i]+r[i]>>1;
if(y<=mid) return query(x,y,i<<1);
if(x>mid) return query(x,y,i<<1|1);
return query(x,y,i<<1)*query(x,y,i<<1|1);
}
inline void change(int i,mat k) {
d[i]=k;i>>=1;
while(i) {d[i]=d[i<<1]*d[i<<1|1];i>>=1;}
}
}t[128];
void build(int x,int y,int i) {
l[i]=x,r[i]=y;
if(x==y) {
int k=id[x];pos[k]=i;
for(re int o=head[k];o;o=e[o].nxt)
if(deep[e[o].v]>deep[k]&&son[k]!=e[o].v)
for(re int j=0;j<len;j++) {
dp[k][j]=(dp[k][j]+dp[k][j]*dp[e[o].v][j]%mod)%mod,
g[k][j]=(g[k][j]+g[e[o].v][j])%mod,
val[k][j]=(val[k][j]*(dp[e[o].v][j]+1))%mod;
if((dp[e[o].v][j]+1)%mod!=0) h[k][j]=(h[k][j]*(dp[e[o].v][j]+1))%mod;
else tmp[k][j]++;
}
for(re int j=0;j<len;j++)
t[j].d[i].a=t[j].d[i].b=t[j].d[i].c=dp[k][j],
t[j].d[i].d=(g[k][j]+dp[k][j])%mod;
if(son[k]) {
for(re int j=0;j<len;j++)
dp[k][j]=(dp[k][j]+dp[k][j]*dp[son[k]][j]%mod)%mod,
g[k][j]=(g[k][j]+g[son[k]][j])%mod;
}
for(re int j=0;j<len;j++) g[k][j]=(g[k][j]+dp[k][j])%mod;
return;
}
int mid=x+y>>1;
build(mid+1,y,i<<1|1),build(x,mid,i<<1);
for(re int j=0;j<len;j++) t[j].pushup(i);
}
signed main() {
n=read(),len=read();
for(re int x,i=1;i<=n;i++) {
x=read();dp[i][x]++;Fwt(dp[i],0);
for(re int j=0;j<len;j++) h[i][j]=val[i][j]=1,p[i][j]=dp[i][j];
}
inv[1]=1;
for(re int i=2;i<mod;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for(re int x,y,i=1;i<n;i++)
x=read(),y=read(),add(x,y),add(y,x);
deep[1]=1;dfs1(1);bot[1]=dfs2(1,1);
build(1,n,1);char op[10];int x,v,X;
Q=read();
while(Q--) {
scanf("%s",op);
if(op[0]=='Q') {
for(re int i=0;i<len;i++) {
mat G=t[i].query(1,dfn[bot[1]],1);
S[i]=G.d;
}
Fwt(S,1);
x=read();printf("%d\n",S[x]);
}else {
X=read(),v=read();
memset(H,0,sizeof(H));
H[v]++;Fwt(H,0);
for(re int i=0;i<len;i++) {
x=X;if(H[i]==p[x][i]) continue;
mat pre=t[i].query(dfn[top[x]],dfn[bot[x]],1);
mat now=t[i].d[pos[x]];
now.a=H[i]*val[x][i]%mod;
now.d=(now.d-now.b+now.a+mod)%mod;
now.b=now.c=now.a;
t[i].change(pos[x],now);
if(top[x]==1) continue;
mat G=t[i].query(dfn[top[x]],dfn[bot[x]],1);
x=fa[top[x]];
while(1) {
now=t[i].d[pos[x]];
if(inv[pre.b+1]) {
now.a=now.a*inv[pre.b+1]%mod;
val[x][i]=(val[x][i]*inv[pre.b+1])%mod;
h[x][i]=(h[x][i]*inv[pre.b+1])%mod;
}
else {
tmp[x][i]--;
if(!tmp[x][i])
val[x][i]=h[x][i],now.a=(h[x][i]*p[x][i])%mod;
}
now.a=(now.a+now.a*G.b%mod)%mod;
now.d=(now.d-now.b+now.a+mod)%mod;
now.d=(now.d-pre.d+G.d+mod)%mod;
now.b=now.c=now.a;
val[x][i]=(val[x][i]*(G.b+1))%mod;
if((G.b+1)%mod==0) tmp[x][i]++;
else h[x][i]=(h[x][i]*(G.b+1))%mod;
pre=t[i].query(dfn[top[x]],dfn[bot[x]],1);
t[i].change(pos[x],now);
if(top[x]==1) break;
G=t[i].query(dfn[top[x]],dfn[bot[x]],1);
x=fa[top[x]];
}
}
for(re int i=0;i<len;i++) p[X][i]=H[i];
}
}
return 0;
}
UPD
在\(loj\)上发现了洛谷上卡树剖的数据的生成器,发现这个数据会反复操作同一个点
这不就好办了吗,我们开一个栈把修改存下来,每次遇到询问就弹栈,对于一个点只在其第一次出栈时修改
之后发现有一些取模没有必要,去掉之后就能卡过洛谷数据啦
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const int maxn=30005;
const int mod=10007;
const int Inv=5004;
inline int read() {
char c=getchar();int x=0;while(c<'0'||x>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
struct E{int v,nxt;}e[maxn<<1];
struct mat{int a,b,c,d;};
int n,num,len,m,Q,__;
int head[maxn],son[maxn],sum[maxn],deep[maxn],fa[maxn],top[maxn],S[128],H[128],inv[mod+5];
int bot[maxn],dp[maxn][128],g[maxn][128],dfn[maxn],id[maxn],pos[maxn],val[maxn][128];
int h[maxn][128],tmp[maxn][128],p[maxn][128];
int l[maxn*3],r[maxn*3],a[maxn],b[maxn],Top,vis[maxn];
inline void add(int x,int y) {
e[++num].v=y;e[num].nxt=head[x];head[x]=num;
}
inline void Fwt(int *f,int o) {
for(re int i=2;i<=len;i<<=1)
for(re int ln=i>>1,l=0;l<len;l+=i)
for(re int x=l;x<l+ln;++x) {
int g=f[x],h=f[x+ln];
f[x]=(g+h)%mod,f[x+ln]=(g-h+mod)%mod;
if(o) f[x]=(f[x]*Inv)%mod,f[x+ln]=(f[x+ln]*Inv)%mod;
}
}
void dfs1(int x) {
sum[x]=1;
for(re int i=head[x];i;i=e[i].nxt) {
if(deep[e[i].v]) continue;
deep[e[i].v]=deep[x]+1,fa[e[i].v]=x;
dfs1(e[i].v);sum[x]+=sum[e[i].v];
if(sum[e[i].v]>sum[son[x]]) son[x]=e[i].v;
}
}
int dfs2(int x,int topf) {
top[x]=topf,dfn[x]=++__,id[__]=x;
if(son[x]) dfs2(son[x],topf);
else return bot[x]=x;
for(re int i=head[x];i;i=e[i].nxt)
if(!top[e[i].v]) bot[e[i].v]=dfs2(e[i].v,e[i].v);
return bot[x]=bot[son[x]];
}
inline mat operator*(mat a,mat b) {
mat c;
c.a=a.a*b.a%mod;
c.b=(a.b+a.a*b.b)%mod;
c.c=(b.c+b.a*a.c)%mod;
c.d=(b.b*a.c%mod+a.d+b.d)%mod;
return c;
}
struct Segment_Tree {
mat d[maxn*3];
inline void pushup(int i) {d[i]=d[i<<1]*d[i<<1|1];}
mat query(const int x,const int y,re int i) {
if(x<=l[i]&&y>=r[i]) return d[i];
int mid=l[i]+r[i]>>1;
if(y<=mid) return query(x,y,i<<1);
if(x>mid) return query(x,y,i<<1|1);
return query(x,y,i<<1)*query(x,y,i<<1|1);
}
inline void change(re int i,mat k) {
d[i]=k;i>>=1;
while(i) {pushup(i);i>>=1;}
}
}t[128];
void build(int x,int y,int i) {
l[i]=x,r[i]=y;
if(x==y) {
int k=id[x];pos[k]=i;
for(re int o=head[k];o;o=e[o].nxt)
if(deep[e[o].v]>deep[k]&&son[k]!=e[o].v)
for(re int j=0;j<len;j++) {
dp[k][j]=(dp[k][j]+dp[k][j]*dp[e[o].v][j])%mod,
g[k][j]=(g[k][j]+g[e[o].v][j])%mod,
val[k][j]=(val[k][j]*(dp[e[o].v][j]+1))%mod;
if((dp[e[o].v][j]+1)%mod!=0) h[k][j]=(h[k][j]*(dp[e[o].v][j]+1))%mod;
else tmp[k][j]++;
}
for(re int j=0;j<len;j++)
t[j].d[i].a=t[j].d[i].b=t[j].d[i].c=dp[k][j],
t[j].d[i].d=(g[k][j]+dp[k][j])%mod;
if(son[k]) {
for(re int j=0;j<len;j++)
dp[k][j]=(dp[k][j]+dp[k][j]*dp[son[k]][j])%mod,
g[k][j]=(g[k][j]+g[son[k]][j])%mod;
}
for(re int j=0;j<len;j++) g[k][j]=(g[k][j]+dp[k][j])%mod;
return;
}
int mid=x+y>>1;
build(mid+1,y,i<<1|1),build(x,mid,i<<1);
for(re int j=0;j<len;j++) t[j].pushup(i);
}
inline void modify(int X,int v) {
memset(H,0,sizeof(H));
H[v]++;Fwt(H,0);int x;
for(re int i=0;i<len;i++) {
x=X;if(H[i]==p[x][i]) continue;
mat pre=t[i].query(dfn[top[x]],dfn[bot[x]],1);
mat now=t[i].d[pos[x]];
now.a=H[i]*val[x][i]%mod;
now.d=(now.d-now.b+now.a+mod)%mod;
now.b=now.c=now.a;
t[i].change(pos[x],now);
if(top[x]==1) continue;
mat G=t[i].query(dfn[top[x]],dfn[bot[x]],1);
x=fa[top[x]];
while(1) {
now=t[i].d[pos[x]];
if(inv[pre.b+1]) {
now.a=now.a*inv[pre.b+1]%mod;
val[x][i]=(val[x][i]*inv[pre.b+1])%mod;
h[x][i]=(h[x][i]*inv[pre.b+1])%mod;
}
else {
tmp[x][i]--;
if(!tmp[x][i])
val[x][i]=h[x][i],now.a=(h[x][i]*p[x][i])%mod;
}
now.a=(now.a+now.a*G.b)%mod;
now.d=(now.d-now.b+now.a+mod)%mod;
now.d=(now.d-pre.d+G.d+mod)%mod;
now.b=now.c=now.a;
val[x][i]=(val[x][i]*(G.b+1))%mod;
if((G.b+1)%mod==0) tmp[x][i]++;
else h[x][i]=(h[x][i]*(G.b+1))%mod;
pre=t[i].query(dfn[top[x]],dfn[bot[x]],1);
t[i].change(pos[x],now);
if(top[x]==1) break;
G=t[i].query(dfn[top[x]],dfn[bot[x]],1);
x=fa[top[x]];
}
}
for(re int i=0;i<len;i++) p[X][i]=H[i];
}
signed main() {
n=read(),len=read();
for(re int x,i=1;i<=n;i++) {
x=read();dp[i][x]++;Fwt(dp[i],0);
for(re int j=0;j<len;j++) h[i][j]=val[i][j]=1,p[i][j]=dp[i][j];
}
inv[1]=1;
for(re int i=2;i<mod;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for(re int x,y,i=1;i<n;i++)
x=read(),y=read(),add(x,y),add(y,x);
deep[1]=1;dfs1(1);bot[1]=dfs2(1,1);
build(1,n,1);char op[10];int x,v,X;
Q=read();
while(Q--) {
scanf("%s",op);
if(op[0]=='Q') {
int mid=Top;
while(Top) {
if(!vis[a[Top]]) modify(a[Top],b[Top]);
vis[a[Top]]=1;
Top--;
}
for(re int i=1;i<=mid;i++) vis[a[i]]=0;
for(re int i=0;i<len;i++) {
mat G=t[i].query(1,dfn[bot[1]],1);
S[i]=G.d;
}
Fwt(S,1);
x=read();printf("%d\n",S[x]);
}else {
x=read(),v=read(),a[++Top]=x,b[Top]=v;
}
}
return 0;
}
[SDOI2017]切树游戏的更多相关文章
- 【BZOJ4911】[SDOI2017]切树游戏(动态dp,FWT)
[BZOJ4911][SDOI2017]切树游戏(动态dp,FWT) 题面 BZOJ 洛谷 LOJ 题解 首先考虑如何暴力\(dp\),设\(f[i][S]\)表示当前以\(i\)节点为根节点,联通子 ...
- BZOJ4911: [Sdoi2017]切树游戏
BZOJ 4911 切树游戏 重构了三次.jpg 每次都把这个问题想简单了.jpg 果然我还是太菜了.jpg 这种题的题解可以一眼秒掉了,FWT+动态DP简直是裸的一批... 那么接下来,考虑如何维护 ...
- 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]表示联通子树的最浅点在 ...
- 洛谷 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}= ...
随机推荐
- Java的Collection.sort()方法
Java中的Collection.sort()方法能使用泛型对对象的变量进行排序,下面是两种方法. 文件名:student.java import java.util.*; import com.su ...
- C#设计模式--代理模式(学习Learning hard 设计模式笔记)
class Program { static void Main(string[] args) { //创建一个代理对象 并发出请求 Person proxy = new Friend(); prox ...
- html5备忘录——张鑫旭
by zhangxinxu from http://www.zhangxinxu.com本文地址:http://www.zhangxinxu.com/wordpress/?p=1544 一.前言兼图片 ...
- Review——JS的异步与同步
一.概念 同步(synchronous):指在js的主线程上,所有任务被依次执行: 异步(asynchronous):指任务不进入主线程,进入任务队列(task):当“任务队列”通知主线程,异步任务才 ...
- ASP.NET MVC 简单事务添加
ASP.NET MVC 简单事务 //实例化查询上下文 using ( BookStoreEntities db = new BookStoreEntities()) { //找到需要价格和名称的数据 ...
- BZOJ4475 [Jsoi2015]子集选取
Description 有一些\(\{1\dots n\}\)的子集\(A_{i,j}, 1\leq j\leq i\leq k\)共\(\frac{k(k+1)}2\)个,满足\(A_{i,j}\s ...
- EF_CRUD
- A Complete Tutorial to Learn Data Science with Python from Scratch
A Complete Tutorial to Learn Data Science with Python from Scratch Introduction It happened few year ...
- Android 开发中三种多线程
在开发工程中线程可以帮助我们提高运行速度,Android开发中我知道的线程有四个一个是老生长谈的Thread,第二个是asyncTask,第三个:TimetTask,第四个是Looper,四个多线程各 ...
- dev gridview指定单元格cell获取坐标
DevExpress.XtraGrid.Views.Grid.ViewInfo.GridViewInfo Info2 = gvQueryResult.GetViewInfo() as DevExpre ...