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