《CF464E》 解题报告

时间:2023-08-26
本文章向大家介绍《CF464E》 解题报告,主要内容包括加法、比大小、使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

好题。

今天模拟赛出到这题的究极强化版,于是来写一下这题,也就是弱化版(你管这叫弱化版)

其实思路不难,但是比较有趣。

首先我们肯定是要维护这么一个高精的,但是如果直接维护肯定鉴定为寄。

考虑其他维护方法。

首先要知道 \(dij\) 是肯定不能少的。

我们思考 \(dij\) 主要有两个操作。

  • 加法

  • 比大小

也就是说如果我们可以在时间复杂度允许范围内做出这些操作,这题就做完了。

加法

考虑到所有边的权值都是 \(2^x\) 的特殊性。

我们考虑将他变成二进制进行维护。

而我们每走一条边就要在一位上加 \(1\)

这就导致了要进行进位。

而如果要进位我们需要将连续一段 \(1\) 变成 \(0\) ,再将一个 \(0\) 变成 \(1\)

考虑到这是区间推平和单点修改,我们可以使用线段树维护,但是由于对每个点都要维护,所以我们考虑使用主席树。

对于区间推平,我们先建出一个全部节点权值为 \(0\) 的主席树,然后将对应区间上的节点接到我们所需区间即可。

对于单点修改没什么好说的。

我们加法一共要执行 \(m\) 次,也就是说加法的总时间复杂度是 \(m\log n\)

比大小

直接比,肯定不行。

考虑哈希,找到第一个不同的位置,然后比较两数大小即可,直接线段树二分即可。

我们使用优先队列要耗费 \(q\log n\) 的时间复杂度,加上线段树二分,就是 \(q\log n\log |V|\) 的时间复杂度。

总时间复杂度 \(m\log n+q\log n\log |V|\)

空间复杂度 \(m\log |V|\)

点击查看代码
#include<bits/stdc++.h>
typedef long long LL;

using namespace std;
const int MAXN=1e5+100,N=1e5+20,MODD=1e9+7,NN=MAXN*77;
int n,m,S,T,tot;
int pw[N];
struct daduoli {
	int f,t,w;
}que[MAXN*2];
int cnt,h[MAXN];
void add(int f,int t,int w) {
	que[++cnt].f=h[f];
	que[cnt].t=t;
	que[cnt].w=w;
	h[f]=cnt;
} 
int rt[MAXN],sf[MAXN],pre[MAXN];
struct trr {
	int lc[NN],rc[NN],sum[NN],sz[NN];
	LL hs[NN];
	void zt(int x,int v) {
		lc[x]=lc[v]; sum[x]=sum[v];
		rc[x]=rc[v]; sz[x]=sz[v]; hs[x]=hs[v];
	}
	void psup(int x) {
		sum[x]=sum[lc[x]]+sum[rc[x]];
		hs[x]=((LL)pw[sz[lc[x]]]*hs[rc[x]]%MODD+hs[lc[x]])%MODD;
	}
	void build(int &x,int l,int r) {
		x=++tot;
		sz[x]=r-l+1; hs[x]=0;
		if(l==r) return ;
		int mid=(l+r)/2;
		build(lc[x],l,mid);
		build(rc[x],mid+1,r);
		psup(x);
	}
	void insert(int &x,int u,int l,int r,int w) {
		x=++tot; zt(x,u);
		if(l==r) {
			++hs[x];
			++sum[x];
			return ;
		}
		int mid=(l+r)/2;
		if(w<=mid) insert(lc[x],lc[u],l,mid,w);
		if(w>mid) insert(rc[x],rc[u],mid+1,r,w);
		psup(x);
	}
	void update(int &x,int u,int ori,int l,int r,int L,int R) {
		if(l>=L&&r<=R) {
			x=ori;
			return ;
		}
		x=++tot; zt(x,u);
		int mid=(l+r)/2;
		if(L<=mid) update(lc[x],lc[u],lc[ori],l,mid,L,R);
		if(R>mid) update(rc[x],rc[u],rc[ori],mid+1,r,L,R);
		psup(x);
	}
	int query(int x,int l,int r,int L,int R) {
		if(l>R||r<L) return 0;
		if(l>=L&&r<=R) return sum[x];
		int mid=(l+r)/2;
		return (query(lc[x],l,mid,L,R)+query(rc[x],mid+1,r,L,R));
	}
	int QUERY(int x,int l,int r,int p,int num) {
		if(l==r) return l;
		int mid=(l+r)/2;
		if(sum[lc[x]]>=mid-p+1+num) return QUERY(rc[x],mid+1,r,p,num-sum[lc[x]]);
		return QUERY(lc[x],l,mid,p,num);
	}
	void mdf(int &x,int lst,int w) {
		int c=(w>0?query(lst,0,N,0,w-1):0);
		int y=QUERY(lst,0,N,w,c);
		if(y>w) update(x,lst,rt[0],0,N,w,y-1);
		else x=lst;
		insert(x,x,0,N,y);
	}
	bool cmp(int a,int b,int l,int r) {
		if(l==r) return sum[a]>sum[b];
		int mid=(l+r)/2;
		if(hs[rc[a]]==hs[rc[b]]) return cmp(lc[a],lc[b],l,mid);
		return cmp(rc[a],rc[b],mid+1,r);
	}
} T1;

void init() {
	pw[0]=1;
	for(int i=1;i<=N-1;++i) pw[i]=pw[i-1]*2%MODD;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i) {
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w);
		add(v,u,w);
	}
}
struct ddl {
	int id,rt;
	bool operator <(const ddl &a)const {
		return T1.cmp(rt,a.rt,0,N);
	}
};
priority_queue<ddl> q;
void dij() {
	T1.build(rt[0],0,N); rt[S]=rt[0]; q.push({S,rt[S]});
	while(!q.empty()) {
		int u=q.top().id; q.pop();
		if(sf[u]) continue; sf[u]=1;
		if(u==T) break;
		for(int i=h[u];i;i=que[i].f) {
			int t=que[i].t;
			T1.mdf(rt[n+1],rt[u],que[i].w);
			if(!rt[t]||T1.cmp(rt[t],rt[n+1],0,N)) {
				rt[t]=rt[n+1]; 
				pre[t]=u;
				q.push({t,rt[t]});
			}
		}
	}
	if(!sf[T]) {
		puts("-1");
		exit(0);
	}
	else printf("%lld\n",T1.hs[rt[T]]);
	int x=T,cnt=0;
	vector<int> e;
	while(x) {
		++cnt;
		e.push_back(x);
		x=pre[x];
	}
	printf("%d\n",cnt);
	for(int len=e.size()-1,i=len;i>=0;--i) {
		printf("%d ",e[i]);
	}
}
int main () {
	init();
	scanf("%d%d",&S,&T); 
	dij();
	return 0;
}
/*
4 3
1 2 0
2 3 0
3 4 0
1 4
*/

原文地址:https://www.cnblogs.com/ddl1no2home/p/17658542.html