做题记录一
都成老年选手了,能记点就记点吧。
9.10
BZOJ3786 星际探索
玛丽题,跑出括号序后成区间问题,平衡树维护区间移动,加法。
对于移动一段区间,平衡树需要维护节点内正的贡献数量,方便区间加法,然后区间移动的变化量要算清。
写细点,首先跑出括号序 \(dfnl,dfnr\),左端点是正贡献,右端点是负贡献,每个子树对应的都是一段区间,换父亲就是把这个区间移动到新父亲的管辖区间里,区间的移动自然不会有交,所以可以在平衡树上维护这一操作,先把每个点的括号序插入到平衡树中,区间移动就相当于对区间加,如何在区间修改的情况下快速查询每个点新的括号序,直接暴力跳父亲,一路 pushdown 下来即可,树高是 \(\log\) 的,所以不影响复杂度。再考虑子树加,记录一段区间内正贡献的数量,直接区间加即可。最后考虑查询,题目保证根不会变,所以对于 \(u\) 点的查询,就是区间 \([1,dfnl_u]\) 的和。
点击查看代码
#include<bits/stdc++.h>
#define int long long
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=2e5+10;
std::mt19937 myrand(1);
int n,m,dfc,dfnl[N],dfnr[N],v[N];
std::vector<int> e[N];
inline void dfs(int u,int fa){
dfnl[u]=++dfc;
for(int v:e[u])if(v!=fa)dfs(v,u);
dfnr[u]=++dfc;
}
struct FHQ{
int root,x,y,cnt,size[N],ls[N],rs[N],sum[N],val[N],w[N],pd[N],tagp[N],tagd[N],rnd[N],num[N],fa[N];
inline int new_node(int k,int W,int P){val[++cnt]=k,w[cnt]=sum[cnt]=W,num[cnt]=pd[cnt]=P,size[cnt]=1,rnd[cnt]=myrand();return cnt;}
inline void update(int p){fa[ls[p]]=p,fa[rs[p]]=p; size[p]=size[ls[p]]+rs[p]+1;sum[p]=sum[ls[p]]+sum[rs[p]]+w[p];num[p]=num[ls[p]]+num[rs[p]]+pd[p];}
inline void pushdown(int p){
fa[ls[p]]=p,fa[rs[p]]=p;
if(tagp[p]){tagp[ls[p]]+=tagp[p],tagp[rs[p]]+=tagp[p],val[ls[p]]+=tagp[p],val[rs[p]]+=tagp[p];}
if(tagd[p]){tagd[ls[p]]+=tagd[p],tagd[rs[p]]+=tagd[p],sum[ls[p]]+=num[ls[p]]*tagd[p],sum[rs[p]]+=num[rs[p]]*tagd[p];w[ls[p]]+=pd[ls[p]]*tagd[p],w[rs[p]]+=pd[rs[p]]*tagd[p];}
return tagd[p]=tagp[p]=0,void();
}
inline void split(int fx,int fy,int p,int k,int &x,int &y){
if(!p){return x=y=0,void();}
pushdown(p);
if(val[p]<=k)x=p,fa[x]=fx,fx=fx,split(fx,fy,rs[p],k,rs[p],y);
else y=p,fa[y]=fy,fy=y,split(fx,fy,ls[p],k,x,ls[p]);
update(p);
}
inline int merge(int u,int v){
if(!u||!v){return u|v;}
if(rnd[u]<rnd[v]){pushdown(u);rs[u]=merge(rs[u],v);fa[rs[u]]=u,update(u);return u;}
else{pushdown(v);ls[v]=merge(u,ls[v]);fa[ls[v]]=v;update(v);return v;}
}
inline void F(int x){if(!x)return;F(fa[x]);pushdown(x);}
inline int find(int u){F(fa[u]);return val[u];}
inline void insert(int k,int W,int P){split(0,0,root,k,x,y);root=merge(merge(x,new_node(k,W,P)),y);fa[root]=0;}
inline void move(int u,int f){
int l=find(2*u-1),r=find(2*u),pos=find(2*f-1),z=0;
int len=r-l+1,d;
if(l>pos){
d=pos+1-l;
split(0,0,root,r,x,y);split(0,0,x,l-1,x,z);
tagp[z]+=d,val[z]+=d;
int c=0;split(0,0,x,pos,x,c);
tagp[c]+=len,val[c]+=len;
root=merge(merge(merge(x,z),c),y);
}else{
d=pos-r;
split(0,0,root,r,x,y);split(0,0,x,l-1,x,z);
tagp[z]+=d,val[z]+=d;
int c=0;split(0,0,y,pos,c,y);
tagp[c]-=len,val[c]-=len;
root=merge(merge(merge(x,c),z),y);
}fa[root]=0;
}
inline void add(int u,int d){
int l=find(2*u-1),r=find(2*u);
int z;split(0,0,root,r,x,y);split(0,0,x,l-1,x,z);
sum[z]+=num[z]*d,w[z]+=pd[z]*d,tagd[z]+=d;
root=merge(merge(x,z),y);fa[root]=0;
}
inline int ask(int u){
int l=find(2*u-1);
split(0,0,root,l,x,y);int res=sum[x];
root=merge(x,y);fa[root]=0;
return res;
}
}s;
signed main(){
// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
n=read();for(int i=2;i<=n;++i){int x=read();e[x].emplace_back(i);e[i].emplace_back(x);}
for(int i=1;i<=n;++i){v[i]=read();}
dfs(1,0);
for(int i=1;i<=n;++i)s.insert(dfnl[i],v[i],1),s.insert(dfnr[i],-v[i],-1);
m=read();
for(int i=1;i<=m;++i){
char ch=getchar();while(ch<'A'||ch>'Z')ch=getchar();
if(ch=='Q')std::cout<<s.ask(read())<<'\n';
if(ch=='C'){int u=read(),fa=read();s.move(u,fa);}
if(ch=='F'){int u=read(),d=read();s.add(u,d);}
}
}
P11036 【MX-X3-T3】「RiOI-4」GCD 与 LCM 问题
cpa 给的题,分下奇偶,设 \(\gcd(c,d)=x\) 后就差不多没东西了,sol。
9.11
P2257 YY的GCD
这个题应该就是最板的莫反吧。随便退下式子后考虑函数 \(f\) 的性质,筛出来就做完了。
数表
平凡推下式子之后,发现限制很大,但是发现 \(n\) 比较小,考虑离线二维数电,BIT 维护做完了。
约数个数和
平凡推下式子后,可以做到 \(\mathcal{O}(n^{\frac{3}{4}})\),然后在把约数倒进去,筛一下约数个数做完了。
upd on 9.19:做到更新这个博有点困难了。
9.13
发现不会差分约束,所以速通了。
糖果
朴素的 spfa 跑不动,发现边权只有 \(0\) 和 \(-1\),把边权转为正的后找正环就行,缩点后拓扑。
9.14
打了模拟赛,学了 nim-k。
9.19
P2607 [ZJOI2008] 骑士
基环树森林,考虑每个连通块,处理环上的树后,在环上 DP 即可,答案是所有连通块的和。
10.16
History
不考虑回溯操作,首先 \(y\) 如果为奇数答案一定是 \(0\),否则去找 \(x\) 的 \(\frac{y}{2}\) 级祖先,倍增处理,考虑对于每一个深度按 dfs 序为下标建立一棵线段树,查的时候直接查祖先范围内的点即可。
加上回溯操作相当于让每一个查询在特定的时间询问,回溯相当于加边,整个操作序列构成了一棵树,直接 dfs 一遍即可,每个 1 操作最多做两次。时间复杂度 \(\mathcal{O}(n\log n)\)。
CF1139D
设 \(f_i\) 表示目前 gcd 为 \(i\),期望还需要多少次才能使 gcd 为 \(1\),设 \(num_{i,j}\) 表示 \([1,m]\) 中与 \(i\) 的 gcd 为 \(j\) 的数的个数,转移方程为 \(\large f_i=1+\frac{\sum_{j\mid i}f_{j}num_{i,j}}{n}\),两边都有目标,简单化简一下,\(\Large f_i=\frac{n+\sum_{j\mid i\& j\not=i}f_jnum_{i,j}}{n-num_{i,i}}\),边界值有 \(\large f_1=0\),现在只剩最后一个问题了,如何求得 \(num_{i,j}\),直接上容斥即可。
感觉不可能做到更新这个博了。

浙公网安备 33010602011771号