修仙录 3.12

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_41709770/article/details/88418174

冷静分析,丝毫不乱
今天考试又凉了QAQ QWQ QXQ
不过好像这次比较难 反正我就是爆零
鲍丽芬好难拿。

还是先把昨天剩下的题改了吧
今天的随缘了QAQ


jzoj 6050 树上四次求和

https://jzoj.net/senior/#main/show/6050
很显然的四次求和,暴力都跑不了
话说这文件名是认真的吗
那就考虑求和用递推,每次通过加上相差的增量,即 i 对答案的贡献
于是就是一顿混乱的四个 \sum 划划划
过程省略,感性分析吧

不妨把 dis(i, j) 转化为 dep(i) + dep(j) − 2 ∗ dep(lca(i, j))。
设 fi 为询问 i 的答案,我们考虑增量,fi 从 fi−1 过来时多了右端点为 i 的贡献,而右端点为 i
的贡献又是右端点为 i − 1 的贡献加上点 i 和前面的距离乘上缺少的次数。我们发现缺少的次数是
不变的,所以就是要求 j = 1 i d e p ( l c a ( i , j ) ) j \sum_{j=1}^idep(lca(i, j)) ∗ j
这是一个经典问题,可以使用树链剖分在 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的标记(用差分),有交集的时候显然已经可处理了
但是我们发现这样打标记会把一个环脱落的概率变成 p l p^l ,于是需要除 p l 1 p^{l-1}
这时又可以有一种巧妙毒瘤的想法了:每两条边又打一次标记,
这样就在这条边上又打了 l 1 l-1 p 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;
}

改不动了啊啊啊啊啊
第二题斯特林反演——什么鬼啦
第三题又是个看不懂题解的题——凉凉

总之今天还是改了两道题的
下次记得把上面哪个鬼个学了。。。

猜你喜欢

转载自blog.csdn.net/qq_41709770/article/details/88418174