1. 程式人生 > >NOIP2013提高組Day1

NOIP2013提高組Day1

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了。所以也不想寫了。