NOIP2013提高組Day1
阿新 • • 發佈:2018-12-31
NOIP2013Day1
一、轉圈遊戲
按照題意每個人往後m個,那就是(10^k*m+x)%n,然後用快速冪就可以了
二、火柴排隊
看起來很像鬼腳圖,但是有點不一樣,鬼腳圖的上一層是固定的,但這裡兩層都可以動
注意到交換對於交換上面還是交換下面效果都是一樣的,那麼直交換下面就可以了
這樣和鬼腳圖就一模一樣了,排序一下,重新定義一下就行了
三、貨車運輸
我一檔檔寫了過來
對於30分,和七夕是一樣的,只要列舉每條邊作為最小的那個限重,然後判連通,不斷更新即可
對於60分,修改一下上面的,對於每一個詢問,二分限重來求所需的限重。限重滿足線形,越小越容易實現
對於100分,是最大生成樹+倍增+LCA。注意到要使每一條路徑都最長,那麼短的路徑就可以捨去了
用倍增和LCA求兩點在樹上的路徑上的最小權值即可
注意這可能是森林而不是一個完整的樹。我把每棵樹都標上了顏色來區分。如果兩點的顏色不同
那麼他們所在的子樹也不同,就無法連通,直接輸出-1即可
Code:
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
int n,m,q;
struct node{int x,y,z;}G[50005];
bool cmp(node x,node y){return x.z>y.z;}
struct node1{int to,v;};
vector<node1>edge[10005];
struct Tree1{//造出最大生成樹
int fa[10005 ];
int Find(int x){return x==fa[x]?x:fa[x]=Find(fa[x]);}
void solve(){
for(int i=1;i<=n;i++)fa[i]=i;
sort(G+1,G+1+m,cmp);
for(int i=1;i<=m;i++){
int x=Find(G[i].x),y=Find(G[i].y);
if(x==y)continue;
edge[G[i].x].push_back((node1){G[i].y,G[i].z});
edge[G[i].y].push_back((node1){G[i].x,G[i].z});
fa[x]=y;
}
}
}Tree;
int fa[17][10005],dep[10005],dis[17][10005],co[10005],col;
struct Init1{//預處理倍增的東西
bool Q[10005];
void f(int x,int fa1){
dep[x]=dep[fa1]+1;fa[0][x]=fa1;Q[x]=1;co[x]=col;
for(int i=0;i<(int)edge[x].size();i++){
int y=edge[x][i].to,v=edge[x][i].v;
if(fa1==y)continue;
dis[0][y]=v;
f(y,x);
}
}
void Init(){//倍增
for(int j=1;j<17;j++)
for(int i=1;i<=n;i++){
fa[j][i]=fa[j-1][fa[j-1][i]];
dis[j][i]=min(dis[j-1][i],dis[j-1][fa[j-1][i]]);
}
}
void solve(){
col=1;
for(int i=1;i<=n;i++)if(Q[i]==0)f(i,0),col++;
Init();
}
}Init;
int LCA(int x,int y){//LCA
if(dep[x]>dep[y])swap(x,y);
int step=dep[y]-dep[x];
for(int i=0;i<17;i++)
if(step&(1<<i))y=fa[i][y];
if(x==y)return x;
for(int i=16;i>=0;i--)
if(fa[i][x]!=fa[i][y])
x=fa[i][x],y=fa[i][y];
return fa[0][x];
}
int work(int x,int y){//跳著向上
int step=dep[x]-dep[y],mn=1e9;
for(int i=16;i>=0;i--)
if(step&(1<<i))
mn=min(mn,dis[i][x]),x=fa[i][x];
return mn;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)scanf("%d%d%d",&G[i].x,&G[i].y,&G[i].z);
Tree.solve();//建樹
Init.solve();//倍增
scanf("%d",&q);
for(int i=1;i<=q;i++){//每個點的詢問
int x,y;
scanf("%d%d",&x,&y);
if(co[x]!=co[y])puts("-1");
else{
int lca=LCA(x,y);
int L=work(x,lca),R=work(y,lca);
printf("%d\n",min(L,R));
}
}
return 0;
}
一次最簡單的考試。一半的人AK了。所以也不想寫了。