洛谷 P4768 [NOI2018]歸程
洛谷
361行代碼的由來
NOI的簽到題,可憐我在家打了一下午才搞了80分。
分點來講吧~
前6個點
這6個點有兩種做法:
法1:最短路。
這6個點都是離線的,而且只有一種海拔,所以直接最短路。
跑完之後,直接判斷海拔與水位,輸出即可。
不過這些分也並不好拿,spfa會被卡,要用堆優化dijsktra。
法2:離線排序+並查集。
其實這個暴力思想就是正解思想了,很好想到的。
首先跑個最短路求一下1號點到所有點的距離,按照邊權從大到小排序,對於詢問也按照水位線從大到小排一下序。
顯然,當一條邊加入以後,就不可能再刪除,因為水位線只會慢慢降低。
這樣這個問題就可以轉化成,當前詢問下,如果把所有大於當前水位線的邊加入圖中,那麽從起點出發,求它所能到達的點中與1號點距離最小的點即可。
加邊時用並查集維護一下即可:
即對於兩個點a,b在並查集中的祖先x,y,如果dis[x]>dis[y],則令fa[x]=y。
這樣並查集在查詢時,就可以以常數的復雜度找到當前詢問下的答案。註意加邊是在一個查詢開始前,將所有海拔大於水位線的邊加進去。
樹和鏈的5個點
這裏我們可以考慮倍增。
預處理3個ST表:dpt[][],num[][],xiao[][]
分別表示從x點跳\(2^j\)步,從x到向上\(2^j\)個點的花費,最後是從x到向上\(2^j\)個點中海拔最小的點。
所以算法也很簡單,先調到海拔最小的點,然後再倍增求花費。
當然也可以用之前講的離線排序+並查集。
3個離線大點
直接按上面說的離線並查集即可。
15和16兩個在線小點
離線並查集不管用了?別擔心。
因為數據範圍小,直接在線,每次詢問用一個新的並查集維護。
反正能過。
正解
其實上面講的也差不多了。
上面的算法最接近正解的就兩種:
第1種:離線+一個並查集。這樣可以處理離線的大數據點。
第2種:在線+很多並查集。這樣可以處理在線的小數據點。
結合起來,大概有點可持久化的味道了。
沒錯,這一題就是要用到一種叫可持久化並查集的數據結構。
具體算法應該是kruskal重構樹,這個聽起來就很高級的算法。
但當我們使用 kruskal重構樹的時候,對於每次找出的不同的兩個連通塊的祖先,我們都新建一個點作為兩個祖先的父親,並將當前邊的邊權轉化為新點的點權。
然而,路徑壓縮的時候會讓我們丟失這種辛辛苦苦創造的樹的形狀。。。
因此我們需要在使用並查集維護連通性的同時使用二叉樹來維護樹的形狀。
這樣維護出來的樹就是 kruskal重構樹。
用kruskal重構樹+排序可持久化並查集的做法,就可以A了這道T辣~
超長代碼菌~
#include <bits/stdc++.h>
using namespace std;
typedef pair<long long,int> Pair;
void read(int &aa)
{
aa=0;char c=getchar();bool f=0;
while (c>'9'||c<'0') {if (c=='-') f=1;c=getchar();}
while (c>='0'&&c<='9')
aa=(aa<<3)+(aa<<1)+(c^48),c=getchar();
(f)&&(aa=-aa);
}
const int N=800020;
int n,m,q,k,s;
struct Edge {
int next,cost,high,to;
}e[N<<2];
int last[N],spfaflag;
void add(int x,int y,int c,int h)
{
e[++last[0]].to=y;
e[last[0]].cost=c;e[last[0]].next=last[x];
e[last[0]].high=h;last[x]=last[0];
}
int vis[N];
long long dis[N];
queue <int> Q;
priority_queue< Pair,vector<Pair>,greater<Pair> >que;
void spfa()
{
while (!Q.empty()) Q.pop();
for (int i=1;i<=n;++i) dis[i]=2e9+1;
memset(vis,0,sizeof(vis));
vis[1]=1;Q.push(1);dis[1]=0;
while (!Q.empty()) {
int x=Q.front();Q.pop();vis[x]=0;
for (int i=last[x];i;i=e[i].next) {
int y=e[i].to,c=e[i].cost;
if (dis[y]>dis[x]+c) {
dis[y]=dis[x]+c;
if (!vis[y]) vis[y]=1,Q.push(y);
}
}
}
}
void dijsktra()
{
while (!que.empty()) que.pop();
for (int i=1;i<=n;++i) dis[i]=2e9+1;
memset(vis,0,sizeof(vis));
dis[1]=0;
que.push(make_pair(dis[1],1));
while (!que.empty()) {
Pair now=que.top();que.pop();
if (vis[now.second]) continue;
vis[now.second]=1;
for (int i=last[now.second];i;i=e[i].next)
if (dis[now.second]+e[i].cost<dis[e[i].to]) {
dis[e[i].to]=dis[now.second]+e[i].cost;
if (!vis[e[i].to]) que.push(make_pair(dis[e[i].to],e[i].to));
}
}
}
int bian[N],gao[N];
void chain()
{
int ed,pp,chainflag;
long long ans,preans=0;
read(q);read(k);read(s);
for (int i=1;i<=q;++i) {
chainflag=ans=0;
read(ed);read(pp);
ed=(ed+k*preans-1)%n+1;
pp=(pp+k*preans)%(s+1);
for (int t=ed;t>1;--t)
if (chainflag||gao[t]<=pp)
ans+=bian[t],chainflag=1;
printf("%lld\n",ans);
preans=ans;
}
}
int dep[N],dpt[N][33],xiao[N][33],num[N][33];
void dfs(int x,int d)
{
dep[x]=d;
for (int i=last[x];i;i=e[i].next) {
int y=e[i].to;
if (!dep[y]) {
dpt[y][0]=x;
xiao[y][0]=e[i].high;
num[y][0]=e[i].cost;
dfs(y,d+1);
}
}
}
void init()
{
for (int j=1;j<=24;++j)
for (int i=1;i<=n;++i) {
dpt[i][j]=dpt[dpt[i][j-1]][j-1];
xiao[i][j]=min(xiao[i][j-1],xiao[dpt[i][j-1]][j-1]);
num[i][j]=num[i][j-1]+num[dpt[i][j-1]][j-1];
}
}
int zuo(int x,int gaodu)
{
int ans=0;
for (int j=24;j>=0;--j) {
if (xiao[x][j]>gaodu&&dpt[x][j])
x=dpt[x][j];
}
if (x!=1) {
for (int j=24;j>=0;--j)
if (dpt[x][j])
ans+=num[x][j],x=dpt[x][j];
return ans;
}
return 0;
}
void tree()
{
int ed,pp;
dfs(1,1);init();
read(q);read(k);read(s);
int ans,preans=0;
for (int i=1;i<=q;++i) {
read(ed);read(pp);
ed=(ed+k*preans-1)%n+1;
pp=(pp+k*preans)%(s+1);ans=zuo(ed,pp);
printf("%d\n",ans);preans=ans;
}
}
struct Line {
int from,to,cost,high;
}line[N<<1];
struct ask {
int shui,id,v,res;
}wen[N];
int baba[N];
bool pai1(ask t1,ask t2)
{
return t1.shui>t2.shui;
}
bool pai2(Line t1,Line t2)
{
return t1.high>t2.high;
}
bool pai3(ask t1,ask t2)
{
return t1.id<t2.id;
}
int find(int x)
{
return x!=baba[x]?baba[x]=find(baba[x]):x;
}
void unionset(int x,int y)
{
int u=find(x),v=find(y);
if (u==v) return;
if (dis[u]>dis[v]) baba[u]=v;
else baba[v]=u;
}
void outline()
{
for (int i=1;i<=q;++i) {
read(wen[i].v);read(wen[i].shui);
wen[i].id=i;
}
sort(&wen[1],&wen[1+q],pai1);
sort(&line[1],&line[1+m],pai2);
for (int i=1;i<=n;++i) baba[i]=i;
int now=1;
for (int i=1;i<=q;++i) {
while (now<=m&&line[now].high>wen[i].shui)
unionset(line[now].from,line[now].to),++now;
wen[i].res=dis[find(wen[i].v)];
}
sort(wen+1,wen+1+q,pai3);
for (int i=1;i<=q;++i) printf("%d\n",wen[i].res);
}
void navi_inline()
{
int lastans=0,instead;
sort(line+1,line+1+m,pai2);
for (int j=1;j<=q;++j) {
for (int i=1;i<=n;++i) baba[i]=i;
int now=1;
read(instead);
int v=(instead+k*lastans-1)%n+1;
read(instead);
int p=(instead+k*lastans)%(s+1);
while (now<=m&&line[now].high>p)
unionset(line[now].from,line[now].to),++now;
lastans=dis[find(v)];
printf("%d\n",lastans);
}
}
bool cmp(Line t1,Line t2)
{
return t1.high>t2.high;
}
Line line2[N<<1];
int fa[N],ceng[N],tiao[N][25];
struct eeee {
int to,next;
}ee[N<<1];
int head[N];
void jia(int x,int y)
{
ee[++head[0]].to=y;ee[head[0]].next=head[x];
head[x]=head[0];
}
void shensou(int x,int pa)
{
ceng[x]=ceng[pa]+1,tiao[x][0]=pa;
for (int i=1;i<=24;++i)
tiao[x][i]=tiao[tiao[x][i-1]][i-1];
for (int i=head[x];i;i=ee[i].next) {
int y=ee[i].to;
shensou(y,x);
line2[x].cost=min(line2[x].cost,line2[y].cost);
}
}
int xunwenta(int x,int y)
{
for (int i=24;i>=0;--i)
if (ceng[x]-(1<<i)>0&&line2[tiao[x][i]].high>y)
x=tiao[x][i];
return line2[x].cost;
}
int find2(int x)
{
return x==fa[x]?x:fa[x]=find2(fa[x]);
}
void zhengjie_wo_wu_di_la_hahaha_kruskal()
{
int tott=0,cntt=n;
for (int i=1;i<=(n<<1);++i) fa[i]=i;
sort(line+1,line+m+1,cmp);
for (int i=1;i<=m;++i) {
int u=line[i].from,v=line[i].to;
int fx=find2(u),fy=find2(v);
if (fx!=fy) {
jia(++cntt,fx);
jia(cntt,fy);
fa[fx]=cntt;
fa[fy]=cntt;
line2[cntt].high=line[i].high;
++tott;
}
if (tott+1==n) break;
}
int lastans=0;
shensou(cntt,0);
int tttt;
while (q--) {
read(tttt);
int x=(k*lastans+tttt-1)%n+1;
read(tttt);
int y=(k*lastans+tttt)%(s+1);
printf("%d\n",lastans=xunwenta(x,y));
}
}
int main()
{
int T,x,y,c,h;read(T);
while (T--) {
memset(last,0,sizeof(last));
memset(gao,0,sizeof(gao));
memset(bian,0,sizeof(bian));
memset(dep,0,sizeof(dep));
memset(dpt,0,sizeof(dpt));
memset(xiao,0,sizeof(xiao));
memset(num,0,sizeof(num));
memset(head,0,sizeof(head));
memset(tiao,0,sizeof(tiao));
read(n);read(m);bool flag=1;
int pre=0,mp=2;
for (int i=1;i<=m;++i) {
read(x);read(y);read(c);read(h);
gao[y]=h;bian[y]=c;
if (x+1!=y) flag=0;
if (pre!=h) --mp,pre=h;
add(x,y,c,h);add(y,x,c,h);
line[i].from=x;line[i].to=y;
line[i].cost=c;line[i].high=h;
}
if (mp==1) {
int ed,pp;
read(q);read(k);read(s);dijsktra();
for (int i=1;i<=q;++i) {
spfaflag=0;
read(ed);read(pp);
ed=(ed-1)%n+1;pp=pp%(s+1);
if (pp>=pre) spfaflag=1;
printf("%lld\n",!spfaflag?dis[1]:dis[ed]);
}
continue;
}
if (m+1==n) {
if (flag) chain();
else tree();
continue;
}
read(q);read(k);read(s);
if (!k) {
dijsktra();
outline();
continue;
}
if (k&&n<=1500) {
dijsktra();
navi_inline();
continue;
}
for (int i=n+1;i<=(n<<1);++i)
line2[i].cost=0x3f3f3f3f;
dijsktra();
for (int i=1;i<=n;++i)
line2[i].cost=dis[i];
zhengjie_wo_wu_di_la_hahaha_kruskal();
}
return 0;
}
洛谷 P4768 [NOI2018]歸程