#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);
}
}