#12 CF843D & CF453E & CF1628E

Dynamic Shortest Path

题目描述

点此看题

解法

考虑用 \(\tt dijk\) 求出到每个点的最短路,然后再此基础上修改。由于最短路的增量最多是 \(c\),那么可以考虑把优先队列换成桶,跑最短路的时候就把点放进对应的桶中。

时间复杂度 \(O(q\cdot(n+m))\),暴力但有效的算法。

#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;
const int M = 100005;
#define ll long long
const ll inf = 1e18;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,q,tot,f[M],w[M],a[M],in[M];
ll d[M];queue<int> z[M];
struct edge{int v,c,next;}e[M];
struct node
{
	int u;ll c;
	bool operator < (const node &b) const
		{return c>b.c;}
};
void spfa()
{
	for(int i=2;i<=n;i++) d[i]=inf;
	priority_queue<node> q;q.push({1,0});
	while(!q.empty())
	{
		int u=q.top().u;ll tmp=q.top().c;q.pop();
		if(d[u]<tmp) continue;
		for(int i=f[u];i;i=e[i].next)
		{
			int v=e[i].v,c=w[e[i].c];
			if(d[v]>d[u]+c)
				d[v]=d[u]+c,q.push({v,d[v]});
		}
	}
}
signed main()
{
	n=read();m=read();q=read();
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();w[i]=read();
		e[++tot]=edge{v,i,f[u]},f[u]=tot;
	}
	spfa();
	for(int i=1;i<=n;i++)
		if(d[i]==inf) d[i]=-1;
	while(q--)
	{
		int op=read(),k=read();
		if(op==1)
		{
			printf("%lld\n",d[k]);
			continue;
		}
		for(int i=1;i<=k;i++) w[read()]++;
		for(int i=1;i<=n;i++) a[i]=k+1;
		z[a[1]=0].push(1);
		for(int o=0;o<=k;o++)
		for(;!z[o].empty();)
		{
			int u=z[o].front();z[o].pop();
			if(a[u]!=o) continue;
			for(int i=f[u];i;i=e[i].next)
			{
				int v=e[i].v,c=w[e[i].c];
				if(d[v]+a[v]>d[u]+a[u]+c)
					z[a[v]=d[u]+a[u]+c-d[v]].push(v);
			}
		}
		for(int i=1;i<=n;i++)
			if(a[i]!=k+1) d[i]+=a[i];
	}
}

Little Pony and Lord Tirek

题目描述

点此看题

解法

我这个傻逼完完全全地被教育了,但这确实是道好题!

一定要关注本题的这个特点:询问区间和之后立刻把询问的区间清零,这意味询问和修改是绑定的。考虑从分块的角度入手,那么修改时我们可能要维护一个类似于推平标记的东西。

考虑对于散块可以暴力修改(下放标记);而对于不带标记的整块,由于查询之后会立刻带上推平标记,根据势能法可知暴力修改它复杂度是正确的;对于带标记的整块,我们可能需要 \(O(1)\) 求出答案。

此时关注到值域在 \(10^5\) 范围内,所以可以预处理出 \(f[i][j]\),表示第 \(i\) 个块初始推平,过了 \(j\) 时刻的总和是多少。

那么总复杂度 \(O(n\sqrt n)\)

总结

一定要学会应用分块问题中的预处理技巧;对于询问和修改绑定的题目可以考虑势能法。

#include <cstdio>
#include <iostream>
#include <cmath>
using namespace std;
const int B = 405;
const int M = 100005;
#define ll long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,a[M],b[M],d[M],up[M],tmp[M];
int L[B],R[B],tim[B],cov[B],f[B][M];
void init()
{
	k=min((int)sqrt(n),250);
	for(int i=1;i<=n;i++)
	{
		b[i]=(i-1)/k+1;
		if(!L[b[i]]) L[b[i]]=i;
		R[b[i]]=i;
	}
	for(int i=1;i<=b[n];i++)
	{
		int z=1e5;
		for(int j=0;j<=z;j++) tmp[j]=0;
		for(int j=L[i];j<=R[i];j++) if(d[j])
		{
			int x=up[j]/d[j],y=up[j]%d[j];
			tmp[1]+=d[j];tmp[x+1]-=d[j];
			tmp[x+1]+=y;tmp[x+2]-=y;
		}
		for(int j=0;j<=z;j++) tmp[j]+=tmp[j-1];
		for(int j=0;j<=z;j++)
			tmp[j]+=tmp[j-1],f[i][j]=tmp[j];
	}
}
void down(int t,int x)
{
	if(cov[x])
	{
		for(int i=L[x];i<=R[x];i++) a[i]=0;
		cov[x]=0;
	}
	for(int i=L[x];i<=R[x];i++)
		a[i]=min((ll)up[i],a[i]+1ll*d[i]*(t-tim[x]));
	tim[x]=t;
}
ll ask(int t,int l,int r)
{
	ll res=0;
	if(b[l]==b[r])
	{
		down(t,b[l]);
		for(int i=l;i<=r;i++)
			res+=a[i],a[i]=0;
		return res;
	}
	res+=ask(t,l,R[b[l]]);
	res+=ask(t,L[b[r]],r);
	for(int i=b[l]+1;i<b[r];i++)
	{
		int dt=min(100000,t-tim[i]);
		if(cov[i]) res+=f[i][dt];
		else res+=ask(t,L[i],R[i]);
		tim[i]=t;cov[i]=1;
	}
	return res;
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
		a[i]=read(),up[i]=read(),d[i]=read();
	init();
	m=read();
	while(m--)
	{
		int t=read(),l=read(),r=read();
		printf("%lld\n",ask(t,l,r));
	}
}

Groceries in Meteor Town

题目描述

点此看题

解法

嘿嘿,这道题又被我切掉了。

首先使用原题自动机技能,查找到 Omkar and Tours 这题,然后发现本题也可以最值放缩。我们可以首先计算白点两两之间路径最大边的最大值,其实可以以任意顺序,计算相邻两点之间路径最大边的最大值即可。

不难想到用线段树进行区间覆盖,预处理出整个区间都被覆盖的最大值,合并区间时任取两个点合并即可。问题是 \(O(1)\) 求出两点间的最大边权,可以在 \(\tt kruskal\) 重构树上 \(O(1)\)\(\tt lca\) 即可。

但既然都想到了重构树这道题就没有这么复杂,发现最优的边一定是 白点中 \(\tt dfn\) 序最小、白点中 \(\tt dfn\) 序最大、给定点 三点在重构树上的 \(\tt lca\)(用到了一定虚树思想,也就是覆盖范围中的最浅边),那么线段树维护最小值和最大值即可,甚至不需要合并和 \(O(1)\)\(\tt lca\),时间复杂度 \(O(n\log n)\)

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 600005;
const int inf = 0x3f3f3f3f;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
void write(int x)
{
	if(x>=10) write(x/10);
	putchar(x%10+'0');
}
int n,m,k,p[M],fa[M][20],ch[M][2];
int cnt,w[M],dfn[M],id[M],dep[M];
struct node
{
	int u,v,c;
	bool operator < (const node &b) const
		{return c<b.c;}
}e[M];
int find(int x)
{
	return p[x]==x?x:p[x]=find(p[x]);
}
void kruskal()
{
	sort(e+1,e+n);
	for(int i=1;i<2*n;i++) p[i]=i; 
	for(int i=1;i<n;i++)
	{
		int u=find(e[i].u),v=find(e[i].v),c=e[i].c;
		p[u]=p[v]=fa[u][0]=fa[v][0]=++k;
		ch[k][0]=u;ch[k][1]=v;w[k]=c;
	}
}
void dfs(int u)
{
	dfn[u]=++cnt;id[cnt]=u;
	for(int i=1;i<=19;i++)
		fa[u][i]=fa[fa[u][i-1]][i-1];
	dep[u]=dep[fa[u][0]]+1;
	if(u<=n) return ;
	dfs(ch[u][0]);
	dfs(ch[u][1]);
}
int lca(int u,int v)
{
	if(dep[u]<=dep[v]) swap(u,v);
	for(int i=19;i>=0;i--)
		if(dep[fa[u][i]]>=dep[v])
			u=fa[u][i];
	if(u==v) return u;
	for(int i=19;i>=0;i--)
		if(fa[u][i]^fa[v][i])
			u=fa[u][i],v=fa[v][i];
	return fa[u][0];
}
//segment tree
int mi[M<<2],mx[M<<2],A[M<<2],B[M<<2],cov[M<<2];
void build(int i,int l,int r)
{
	cov[i]=-1;mi[i]=inf;mx[i]=0;
	if(l==r) {A[i]=B[i]=dfn[l];return ;}
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
	A[i]=min(A[i<<1],A[i<<1|1]);
	B[i]=max(B[i<<1],B[i<<1|1]);
}
void cover(int i,int c)
{
	if(c==1) mi[i]=A[i],mx[i]=B[i],cov[i]=1;
	else mi[i]=inf,mx[i]=0,cov[i]=0;
}
void down(int i)
{
	if(cov[i]==-1) return ;
	cover(i<<1,cov[i]);cover(i<<1|1,cov[i]);cov[i]=-1;
}
void upd(int i,int l,int r,int L,int R,int c)
{
	if(L>r || l>R) return ;
	if(L<=l && r<=R) {cover(i,c);return ;}
	int mid=(l+r)>>1;down(i);
	upd(i<<1,l,mid,L,R,c);
	upd(i<<1|1,mid+1,r,L,R,c);
	mi[i]=min(mi[i<<1],mi[i<<1|1]);
	mx[i]=max(mx[i<<1],mx[i<<1|1]);
}
signed main()
{
	n=k=read();m=read();
	for(int i=1;i<n;i++)
		e[i].u=read(),e[i].v=read(),e[i].c=read();
	kruskal();dfs(k);build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		int op=read();
		if(op==3)
		{
			int x=read(),u=mi[1],v=mx[1];
			if(u==inf || (u==dfn[x] && v==dfn[x]))
				{puts("-1");continue;}
			write(w[lca(lca(id[u],id[v]),x)]);
			puts("");
			continue;
		}
		int l=read(),r=read();
		if(op==1) upd(1,1,n,l,r,1);
		if(op==2) upd(1,1,n,l,r,0);
	}
}
posted @ 2022-05-12 19:02  C202044zxy  阅读(479)  评论(0编辑  收藏  举报