LOJ2269. 「SDOI2017」切树游戏 [FWT,动态DP]
思路
显然是要DP的。设\(dp_{u,i}\)表示\(u\)子树内一个包含\(u\)的连通块异或出\(i\)的方案数,发现转移可以用FWT优化,写成生成函数就是这样的:
\]
最后答案是所有DP值的和,于是获得了朴素的\(O(nmQ)\)的做法。(中间运算全部用点值表示)
显然是要用动态DP优化的,我们另外记一个\(S_u\)表示子树的DP值和自己的DP值的和,写成矩阵的形式,就是
dp_u\\S_u\\1
\end{matrix}\right]
=
\left[\begin{matrix}
dp'_u&0&dp'_u\\dp'_u&1&S'_u\\0&0&1
\end{matrix}\right]
\times
\left[\begin{matrix}
dp_v\\S_v\\1
\end{matrix}\right]
\]
(转移的意义:\(dp_u=dp'_u+dp'_udp_v,S_u=S'_u-dp'_u+dp_u+S_v\))
当然,这个只能用来做一个儿子,在多个儿子的时候还是不能直接把矩阵乘起来的。
考虑链剖,把轻儿子的信息合并在一起存在矩阵里,用矩阵加速重链上的转移,就对了。
怎么修改呢?
对于要修改的这个点,看上面的转移方程,发现只有\(x^{val_u}\)有改变,于是除掉之前的改成新的就可以了。
对于上面的点,要改变的是后面的\(dp_v+1\),所以也是把原来的除掉换成新的。
除的时候可能会除0,所以数字要换成\(x\times mod^y\)的记录形式来做除法。这东西一旦有两个数相加就炸了,但你发现只有\(dp_u\)需要做除法,而它只和乘除有关,所以没有问题。
但你这样常数又炸了,所以你还需要发现矩阵\(\left[\begin{matrix}
a&0&b\\c&1&d\\0&0&1
\end{matrix}\right]\)的乘法有封闭性,所以只要维护四个值,就快了。
一开始DP的时候有一个细节。转移的时候一定不能把乘法的括号拆开,不然你就没了。
输出答案的时候有一个细节。正常的转移矩阵应该是后面要乘一个向量才能得到正确解,但你发现转移矩阵右边一列就是那个向量,所以可以直接把所有矩阵乘在一起之后用右边一列的信息。注意此时左边\(a,c\)两个元素已经不知道是什么东西了。(初学动态DP的时候在这上面蒙了好久)
(这题就当是复习动态DP吧,毕竟和FWT没有太大关系……)
代码
由于我空间炸了,需要用short
存东西……
#include<bits/stdc++.h>
clock_t t=clock();
namespace my_std{
using namespace std;
#define pii pair<int,int>
#define fir first
#define sec second
#define MP make_pair
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
#define drep(i,x,y) for (int i=(x);i>=(y);i--)
#define go(x) for (int i=head[x];i;i=edge[i].nxt)
#define templ template<typename T>
#define sz 30303
#define SS 130
#define mod 10007
typedef long long ll;
typedef double db;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
templ inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);}
templ inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
templ inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
templ inline void read(T& t)
{
t=0;char f=0,ch=getchar();double d=0.1;
while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar();
while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar();
if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();}
t=(f?-t:t);
}
template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);}
char __sr[1<<21],__z[20];int __C=-1,__zz=0;
inline void Ot(){fwrite(__sr,1,__C+1,stdout),__C=-1;}
inline void print(register int x)
{
if(__C>1<<20)Ot();if(x<0)__sr[++__C]='-',x=-x;
while(__z[++__zz]=x%10+48,x/=10);
while(__sr[++__C]=__z[__zz],--__zz);__sr[++__C]='\n';
}
void file()
{
#ifdef NTFOrz
freopen("a.in","r",stdin);
#endif
}
inline void chktime()
{
#ifndef ONLINE_JUDGE
cout<<(clock()-t)/1000.0<<'\n';
#endif
}
ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;return ret;}
// inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;}
}
using namespace my_std;
int n,m,mm;
int inv[mod+5];
struct Int{short a,z;short v(){return z?0:a;}};
#define Int(x,y) ((Int){x,y})
Int operator + (Int a,Int b){return Int((a.v()+b.v())%mod,0);}
Int operator - (Int a,Int b){return Int((a.v()-b.v()+mod)%mod,0);};
Int operator * (Int a,Int b){ if (b.v()) a.a=1ll*a.a*b.a%mod; else a.z++; return a; }
Int operator / (Int a,Int b){ if (b.v()) a.a=1ll*a.a*inv[b.a]%mod; else a.z--; return a; }
struct Array
{
Int a[SS];
const Array operator + (const Array &x) const {Array ret;rep(i,0,m-1) ret.a[i]=a[i]+x.a[i];return ret;}
const Array operator - (const Array &x) const {Array ret;rep(i,0,m-1) ret.a[i]=a[i]-x.a[i];return ret;}
const Array operator * (const Array &x) const {Array ret;rep(i,0,m-1) ret.a[i]=a[i]*x.a[i];return ret;}
const Array operator / (const Array &x) const {Array ret;rep(i,0,m-1) ret.a[i]=a[i]/x.a[i];return ret;}
}fwt[SS];
struct Matrix
{
Array a,b,c,d;
const Matrix operator * (const Matrix &x) const {return (Matrix){a*x.a,a*x.b+b,c*x.a+x.c,c*x.b+d+x.d};}
};
void FWT(Array &a,int type)
{
Int p,q,I=Int(ksm(2,mod-2),0);
rep(i,0,mm-1)
for (int mid=1<<i,j=0;j<m;j+=mid<<1)
rep(k,0,mid-1)
{
p=a.a[j+k],q=a.a[j+k+mid];
if (type==1) a.a[j+k]=p+q,a.a[j+k+mid]=p-q;
else a.a[j+k]=(p+q)*I,a.a[j+k+mid]=(p-q)*I;
}
}
int val[sz];
struct hh{int t,nxt;}edge[sz<<1];
int head[sz],ecnt;
void make_edge(int f,int t)
{
edge[++ecnt]=(hh){t,head[f]};
head[f]=ecnt;
edge[++ecnt]=(hh){f,head[t]};
head[t]=ecnt;
}
int dfn[sz],pre[sz],size[sz],son[sz],top[sz],bot[sz],fa[sz],T;
#define v edge[i].t
void dfs1(int x,int f)
{
size[x]=1,fa[x]=f;
go(x) if (v!=f)
{
dfs1(v,x);
size[x]+=size[v];
if (size[v]>size[son[x]]) son[x]=v;
}
}
void dfs2(int x,int fa,int tp)
{
pre[dfn[bot[top[x]=tp]=x]=++T]=x;
if (son[x]) dfs2(son[x],x,tp);
go(x) if (v!=fa&&v!=son[x]) dfs2(v,x,v);
}
Array dp[sz],S[sz];
void dfs(int x,int fa)
{
dp[x]=S[x]=fwt[val[x]];
go(x) if (v!=fa)
{
dfs(v,x);
S[x]=S[x]+dp[x]*dp[v]+S[v];
dp[x]=dp[x]+dp[x]*dp[v];
}
}
#undef v
Matrix tr[sz<<2],tmp[sz];
#define lson k<<1,l,mid
#define rson k<<1|1,mid+1,r
void build(int k,int l,int r)
{
if (l==r)
{
int x=pre[l];Array f,s;f=s=fwt[val[x]];
#define v edge[i].t
go(x) if (v!=fa[x]&&v!=son[x]) s=s+f*dp[v]+S[v],f=f*(fwt[0]+dp[v]);
#undef v
tr[k]=tmp[l]=(Matrix){f,f,f,s};
return;
}
int mid=(l+r)>>1;
build(lson),build(rson);
tr[k]=tr[k<<1]*tr[k<<1|1];
}
void modify(int k,int l,int r,int x)
{
if (l==r) return (void)(tr[k]=tmp[l]);
int mid=(l+r)>>1;
if (x<=mid) modify(lson,x);
else modify(rson,x);
tr[k]=tr[k<<1]*tr[k<<1|1];
}
Matrix query(int k,int l,int r,int x,int y)
{
if (x<=l&&r<=y) return tr[k];
int mid=(l+r)>>1;
if (y<=mid) return query(lson,x,y);
if (x>mid) return query(rson,x,y);
return query(lson,x,y)*query(rson,x,y);
}
#undef lson
#undef rson
void modify(int x,int w)
{
Array p=tmp[dfn[x]].a,s=tmp[dfn[x]].d-p;
p=p/fwt[val[x]];p=p*fwt[w];val[x]=w;
tmp[dfn[x]].a=tmp[dfn[x]].b=tmp[dfn[x]].c=p;tmp[dfn[x]].d=s+p;
while (233)
{
Matrix a=query(1,1,n,dfn[top[x]],dfn[bot[top[x]]]);
modify(1,1,n,dfn[x]);
Matrix b=query(1,1,n,dfn[top[x]],dfn[bot[top[x]]]);
x=fa[top[x]]; if (!x) return;
Matrix &M=tmp[dfn[x]];
Array f0=M.a,s0=M.d;s0=s0-f0;
f0=f0/(fwt[0]+a.b);
f0=f0*(fwt[0]+b.b);
s0=s0-a.d+b.d;
tmp[dfn[x]]=(Matrix){f0,f0,f0,s0+f0};
}
}
int main()
{
file();
rep(i,1,mod-1) inv[i]=ksm(i,mod-2);
read(n,m);mm=log2(m);
rep(i,0,m-1) fwt[i].a[i]=Int(1,0),FWT(fwt[i],1);
rep(i,1,n) read(val[i]);
int x,y;
rep(i,1,n-1) read(x,y),make_edge(x,y);
dfs1(1,0),dfs2(1,0,1),dfs(1,0),build(1,1,n);
int Q;read(Q);char s[15];
while (Q--)
{
cin>>s;
if (s[0]=='C') read(x,y),modify(x,y);
else
{
read(x);
Array ans=query(1,1,n,1,dfn[bot[1]]).d;
FWT(ans,-1);
printf("%d\n",ans.a[x].v());
}
}
return 0;
}
LOJ2269. 「SDOI2017」切树游戏 [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< ...
- 【BZOJ4911】[SDOI2017]切树游戏(动态dp,FWT)
[BZOJ4911][SDOI2017]切树游戏(动态dp,FWT) 题面 BZOJ 洛谷 LOJ 题解 首先考虑如何暴力\(dp\),设\(f[i][S]\)表示当前以\(i\)节点为根节点,联通子 ...
- 洛谷 P3781 - [SDOI2017]切树游戏(动态 DP+FWT)
洛谷题面传送门 SDOI 2017 R2 D1 T3,nb tea %%% 讲个笑话,最近我在学动态 dp,wjz 在学 FWT,而我们刚好在同一天做到了这道题,而这道题刚好又是 FWT+动态 dp ...
- 「SDOI2017」树点涂色 解题报告
「SDOI2017」树点涂色 我sb的不行了 其实一开始有一个类似动态dp的想法 每个点维护到lct树上到最浅点的颜色段数,然后维护一个\(mx_{0,1}\)也就是是否用虚儿子的最大颜色 用个set ...
- BZOJ4911: [Sdoi2017]切树游戏
BZOJ 4911 切树游戏 重构了三次.jpg 每次都把这个问题想简单了.jpg 果然我还是太菜了.jpg 这种题的题解可以一眼秒掉了,FWT+动态DP简直是裸的一批... 那么接下来,考虑如何维护 ...
- LG3781 [SDOI2017]切树游戏
题意 题目描述 小Q是一个热爱学习的人,他经常去维基百科学习计算机科学. 就在刚才,小Q认真地学习了一系列位运算符,其中按位异或的运算符\(\oplus\)对他影响很大.按位异或的运算符是双目运算符. ...
- 「SHOI2014」三叉神经树 解题报告
「SHOI2014」三叉神经树 膜拜神仙思路 我们想做一个类似于动态dp的东西,首先得确保我们的运算有一个交换律,这样我们可以把一长串的运算转换成一块一块的放到矩阵上之类的东西,然后拿数据结构维护. ...
- 「WC2018」州区划分(FWT)
「WC2018」州区划分(FWT) 我去弄了一个升级版的博客主题,比以前好看多了.感谢 @Wider 不过我有阅读模式的话不知为何 \(\text{LATEX}\) 不能用,所以我就把这个功能删掉了. ...
随机推荐
- docker第一章--介绍和安装
- 浅析JavaScript异步
一直以来都知道JavaScript是一门单线程语言,在笔试过程中不断的遇到一些输出结果的问题,考量的是对异步编程掌握情况.一般被问到异步的时候脑子里第一反应就是Ajax,setTimseout...这 ...
- Jmeter学习笔记(十六)——HTTP请求之content-type
一.HTTP请求Content-Type 常见的媒体格式类型如下: text/html : HTML格式 text/plain :纯文本格式 text/xml : XML格式 image/gif :g ...
- MySQL FEDERATED引擎使用示例, 类似Oracle DBLINK(转)
1 引擎说明 本地MySQL数据库要访问远程MySQL数据库的表中的数据, 必须通过FEDERATED存储引擎来实现. 有点类似Oracle中的 数据库链接(DBLINK). 要允许这个存储引擎, 当 ...
- iview Carousel 轮播图自适应宽高;iview 轮播图 图片重叠问题;iview tabs 高度互相影响问题;vue this问题;
最终效果图: 一.轮播图中图片自适应宽高: <Carousel loop v-bind:height="imgHeight+'px'" v-model="caro ...
- sqlserver存储过程里传字段、传字符串,并返回DataTable、字符串,存储过程调用存储过程。
经常需要查一些信息, 想写视图来返回数据以提高效率,但是用试视图不能传参,只好想到改存储过程.记录一下语法,方便以后做项目时候想不起来了用. 1:传字段返回datatable 2: 传字段回一串字符 ...
- Oracle Drop Table
DROP TABLE 使用DROP TABLE语句将表或对象表移动到回收站或从数据库中完全删除表及其所有数据. 注:除非指定purge子句,否则drop table语句不会将表占用的空间释放回表空间供 ...
- [openssl][nginx] 使用openssl模拟ssl/tls客户端测试nginx stream
一 server的配置 nginx # cat conf/nginx.conf daemon off; events { debug_connection ; } stream { upstream ...
- Linux磁盘管理——BIOS和UEFI
参考:BIOS and UEFI - CompTIA A+ 220-901 - 1.1 BIOS and UEFI As Fast As Possible 严格上来说BIOS和UEFI除了在搜索boo ...
- 远程桌面远程代码执行漏洞(CVE-2019-0708)Poc
此漏洞影响范围: Windows 7 Windows Server 2008 R2 Windows Server 2008 Windows 2003 Windows XP Windows 8和Wind ...