冷静分析,丝毫不乱
今天考试又凉了QAQ QWQ QXQ
不过好像这次比较难 反正我就是爆零
鲍丽芬好难拿。
还是先把昨天剩下的题改了吧
今天的随缘了QAQ
jzoj 6050 树上四次求和
https://jzoj.net/senior/#main/show/6050
很显然的四次求和,暴力都跑不了
话说这文件名是认真的吗
那就考虑求和用递推,每次通过加上相差的增量,即 i 对答案的贡献
于是就是一顿混乱的四个
划划划
过程省略,感性分析吧
不妨把 dis(i, j) 转化为 dep(i) + dep(j) − 2 ∗ dep(lca(i, j))。
设 fi 为询问 i 的答案,我们考虑增量,fi 从 fi−1 过来时多了右端点为 i 的贡献,而右端点为 i
的贡献又是右端点为 i − 1 的贡献加上点 i 和前面的距离乘上缺少的次数。我们发现缺少的次数是
不变的,所以就是要求
这是一个经典问题,可以使用树链剖分在 O(nlog2n)的时间内求解。如果使用 lct 可以做到一个 log。
感性理解,理性背板
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=1e5+5;
const int mod=998244353;
int n,q,a[MAXN];
int head[MAXN],to[MAXN*2],next[MAXN*2],cnt;
int dep[MAXN],siz[MAXN],fa[MAXN],son[MAXN],top[MAXN],tot,dfn[MAXN];
long long sum[MAXN],ans[MAXN];
struct linetree{
long long sum[MAXN*4],lazy[MAXN*4];
void up(int p){
sum[p]=(sum[p<<1]+sum[p<<1|1])%mod;
}
void down(int p,int l,int r){
if(!lazy[p]) return ;
int mid=(l+r)>>1,v=lazy[p];
sum[p<<1]=(sum[p<<1]+1ll*v*(mid-l+1)%mod)%mod,lazy[p<<1]=(lazy[p<<1]+v)%mod;
sum[p<<1|1]=(sum[p<<1|1]+1ll*v*(r-mid)%mod)%mod,lazy[p<<1|1]=(lazy[p<<1|1]+v)%mod;
lazy[p]=0;
}
void add(int p,int l,int r,int L,int R,long long v){
if(l>=L&&r<=R) {sum[p]=(sum[p]+1ll*v*(r-l+1)%mod)%mod,lazy[p]=(lazy[p]+v)%mod;return ;}
down(p,l,r);
int mid=(l+r)>>1;
if(L<=mid) add(p<<1,l,mid,L,R,v);
if(mid<R) add(p<<1|1,mid+1,r,L,R,v);
up(p);
}
long long ask(int p,int l,int r,int L,int R){
if(l>=L&&r<=R) return sum[p];
down(p,l,r);
int mid=(l+r)>>1;long long ans=0;
if(L<=mid) ans=(ans+ask(p<<1,l,mid,L,R))%mod;
if(mid<R) ans=(ans+ask(p<<1|1,mid+1,r,L,R))%mod;
return ans;
}
}tr;
void add(int u,int v){
next[++cnt]=head[u],to[cnt]=v,head[u]=cnt;
}
void dfs(int x,int F){
for(int i=head[x];i;i=next[i]){
int y=to[i];
if(y==F) continue;
dep[y]=dep[x]+1,fa[y]=x;
dfs(y,x);
siz[x]+=siz[y];
if(siz[son[x]]<siz[y]) son[x]=y;
}
siz[x]++;
}
void build(int x,int tf){
dfn[x]=++tot,top[x]=tf;
if(son[x]) build(son[x],tf);
for(int i=head[x];i;i=next[i]){
int y=to[i];
if(y==fa[x]||y==son[x]) continue;
build(y,y);
}
}
void add_line(int x,int v){
while(x){
tr.add(1,1,n,dfn[top[x]],dfn[x],v),x=fa[top[x]];
}
}
long long ask_line(int x){
long long ans=0;
while(x){
ans=(ans+tr.ask(1,1,n,dfn[top[x]],dfn[x]))%mod,x=fa[top[x]];
}
return ans;
}
void prework(){
dep[1]=1,dfs(1,0),build(1,1);
for(int i=1;i<=n;i++) sum[i]=(sum[i-1]+1ll*dep[a[i]]*i%mod)%mod;
add_line(a[1],1);
}
long long calc(int x,int id){
long long ans=1ll*id*(id-1)/2%mod*dep[x]%mod;
ans=(ans+sum[id-1])%mod;
ans=(ans-1ll*2*ask_line(x)%mod+mod)%mod;
add_line(x,id);
return ans;
}
int main(){
freopen("sumsumsum.in","r",stdin);
freopen("sumsumsum.out","w",stdout);
cin>>n>>q;
for(int i=1;i<n;i++){
int u,v;scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
prework();
for(int i=2;i<=n;i++) ans[i]=(ans[i-1]+calc(a[i],i))%mod;
for(int i=1;i<=n;i++) ans[i]=(ans[i]+ans[i-1])%mod;
for(int i=1;i<=q;i++){
int k;scanf("%d",&k);
printf("%lld\n",ans[k]);
}
return 0;
}
所以说树链剖分真好用
jzoj 6053 Mas的仙人掌
https://jzoj.net/senior/#main/show/6053
–我好累,我好苦,我是一个马铃薯
没什么特别难的数据结构,就是lca,树上差分什么的
但是这个思维难度是真的头大大大大大大大大大大大
首先,不是所有期望都是需要dp的,凉凉
还可以直接计算对答案的贡献
可以发现加一条边对答案产生的贡献:当与它形成的环上有交集的环都脱落时,1*此时的概率c1就是贡献
于是问题变成了如何在logn的复杂度内求上述概率
对新加的一条边,我们把这个环上的边打上p的标记(用差分),有交集的时候显然已经可处理了
但是我们发现这样打标记会把一个环脱落的概率变成
,于是需要除
,
这时又可以有一种巧妙毒瘤的想法了:每两条边又打一次标记,
这样就在这条边上又打了
个
,最后计算的时候除掉就好了(前缀和优化)
记得lca处的那条边也要打,用一个map存就行
还有特判掉下来概率为0的环
毒瘤毒瘤毒瘤毒瘤
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
using namespace std;
const int MAXN=1e6+5;
const int mod=998244353;
int n,m;
int head[MAXN],to[MAXN*2],next[MAXN*2],cnt;
int fa[MAXN][20],d[MAXN];
long long f1[MAXN][2],f2[MAXN][2],ans;
struct need{
int u,v,p;
}qry[MAXN];
map<pair<int,int>,pair<long long,long long> >P;
void add(int u,int v){
next[++cnt]=head[u],to[cnt]=v,head[u]=cnt;
}
void dfs(int x,int F){
f1[x][0]=f2[x][0]=1;
for(int i=1;i<=17;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=head[x];i;i=next[i]){
int y=to[i];
if(y==F) continue;
d[y]=d[x]+1,fa[y][0]=x;
dfs(y,x);
}
}
int get_lca(int x,int y){
if(d[x]<d[y]) swap(x,y);
for(int i=17;i>=0;i--) if(d[fa[x][i]]>=d[y]) x=fa[x][i];
if(x==y) return x;
for(int i=17;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int find_up(int x,int l){
int y=x;
for(int i=17;i>=0;i--) if(d[x]-d[fa[y][i]]<=l) y=fa[y][i];
return y;
}
long long Pow(long long a,int b){
long long ans=1;
while(b){
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
void dfs_1(int x,int F){
for(int i=head[x];i;i=next[i]){
int y=to[i];
if(y==F) continue;
dfs_1(y,x);
f1[x][0]=f1[x][0]*f1[y][0]%mod,f1[x][1]+=f1[y][1];
f2[x][0]=f2[x][0]*f2[y][0]%mod,f2[x][1]+=f2[y][1];
}
}
void dfs_2(int x,int F){
if(F){
f1[x][0]=f1[x][0]*f1[F][0]%mod,f1[x][1]+=f1[F][1];
f2[x][0]=f2[x][0]*f2[F][0]%mod,f2[x][1]+=f2[F][1];
}
for(int i=head[x];i;i=next[i]){
int y=to[i];
if(y==F) continue;
dfs_2(y,x);
}
}
int main(){
freopen("cactus.in","r",stdin);
freopen("cactus.out","w",stdout);
cin>>n>>m;
for(int i=1;i<n;i++){
int u,v;scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
d[1]=1,dfs(1,0);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&qry[i].u,&qry[i].v,&qry[i].p);
int u=qry[i].u,v=qry[i].v,p=qry[i].p,lca=get_lca(u,v);
long long ny;
if(p) ny=Pow(p,mod-2);
if(p){
f2[u][0]=f2[u][0]*p%mod,f2[v][0]=f2[v][0]*p%mod;
f2[lca][0]=f2[lca][0]*ny%mod*ny%mod;
}
else f2[u][1]++,f2[v][1]++,f2[lca][1]-=2;
int x=find_up(u,d[u]-d[lca]-1),y=find_up(v,d[v]-d[lca]-1);
if(d[u]-d[lca]>=2){
if(p) f1[u][0]=f1[u][0]*p%mod,f1[x][0]=f1[x][0]*ny%mod;
else f1[u][1]++,f1[x][1]--;
}
if(d[v]-d[lca]>=2){
if(p) f1[v][0]=f1[v][0]*p%mod,f1[y][0]=f1[y][0]*ny%mod;
else f1[v][1]++,f1[y][1]--;
}
if(x>y) swap(x,y);
if(u!=lca&&v!=lca){
pair<int,int>o=make_pair(x,y);
if(p) if(P[o].first) P[o].first=P[o].first*p%mod; else P[o].first=p;
else P[o].second++;
}
}
dfs_1(1,0),dfs_2(1,0);
for(int i=1;i<=m;i++){
int u=qry[i].u,v=qry[i].v,p=qry[i].p,lca=get_lca(u,v);
long long c1=Pow(f2[u][0]*f2[v][0]%mod*Pow(f2[lca][0],mod-2)%mod*Pow(f2[lca][0],mod-2)%mod,mod-2);
long long c2=f2[lca][1]*2-f2[u][1]-f2[v][1];
int x=find_up(u,d[u]-d[lca]-1),y=find_up(v,d[v]-d[lca]-1);
if(d[u]-d[lca]>=2){
c1=c1*f1[u][0]%mod*Pow(f1[x][0],mod-2)%mod;
c2+=f1[u][1]-f1[x][1];
}
if(d[v]-d[lca]>=2){
c1=c1*f1[v][0]%mod*Pow(f1[y][0],mod-2)%mod;
c2+=f1[v][1]-f1[y][1];
}
if(x>y) swap(x,y);
if(u!=lca&&v!=lca){
pair<int,int>o=make_pair(x,y);
c1=c1*(P[o].first?P[o].first:1)%mod;
c2+=P[o].second;
}
if(p) c1=c1*p%mod;
else c2++;
if(c2) c1=0;
ans=(ans+(1-p+mod)*Pow(c1,mod-2)%mod)%mod;
}
cout<<ans;
return 0;
}
改不动了啊啊啊啊啊
第二题斯特林反演
——什么鬼啦
第三题又是个看不懂题解的题——凉凉
总之今天还是改了两道题的
下次记得把上面哪个鬼个学了。。。