题解:倍增三连击orz
货车运输
跑一遍最大生成树,然后树上倍增求lca,开个数组预处理路径上的最小值。
注意枚举时的顺序qwq,一个前后顺序搞了快一个小时搞不对
最后查询时从大到小能跳就跳上去。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 const int maxn=100005; 7 const int inf=0x7ffffff; 8 int n, m, q, cnt; 9 int dep[maxn], f[maxn], fa[maxn][20], d[maxn][20];//d数组预处理最小值 10 int head[maxn]; 11 bool vis[maxn]; 12 struct edge{//边 13 int u, v, w; 14 }e[50005]; 15 struct node{//存最大生成树 16 int next, to, k; 17 }a[20005]; 18 void add(int u,int v,int w){//加边操作,注意是a数组 19 cnt++; 20 a[cnt].next=head[u]; 21 a[cnt].to=v; 22 a[cnt].k=w; 23 head[u]=cnt; 24 } 25 bool cmp(edge a,edge b){ 26 return a.w>b.w; 27 } 28 int find(int x){ 29 if(x==f[x]) return x; 30 return f[x]=find(f[x]); 31 } 32 void dfs(int x){ 33 vis[x]=true; 34 for(int i=1; i<=16; i++){ 35 if(dep[x]<(1<<i)) break; 36 fa[x][i]=fa[fa[x][i-1]][i-1]; 37 d[x][i]=min(d[x][i-1], d[fa[x][i-1]][i-1]); 38 } 39 for(int i=head[x]; i; i=a[i].next){ 40 int s=a[i].to; 41 if(vis[s]) continue; 42 fa[s][0]=x; 43 d[s][0]=a[i].k; 44 dep[s]=dep[x]+1; 45 dfs(s); 46 } 47 } 48 int lca(int x,int y){ 49 if(dep[x]<dep[y]) swap(x,y); 50 for(int i=16; i>=0; i--){ 51 if(dep[fa[x][i]]>=dep[y]) x=fa[x][i]; 52 if(x==y) return x; 53 } 54 for(int i=16; i>=0; i--){ 55 if(fa[x][i]!=fa[y][i]){ 56 x=fa[x][i]; 57 y=fa[y][i]; 58 } 59 } 60 if(x==y) return x; 61 return fa[x][0]; 62 } 63 int ask(int x, int f){ 64 int minn=inf; 65 for(int i=16; i>=0; i--){ 66 int t=dep[x]-dep[f];//深度差 67 if(t>=(1<<i)){//能跳就跳上去 68 minn=min(minn,d[x][i]); 69 x=fa[x][i]; 70 } 71 } 72 return minn; 73 } 74 int main(){ 75 memset(d, 127, sizeof(d)); 76 scanf("%d%d",&n,&m); 77 for(int i=1; i<=n; i++) f[i]=i; 78 for(int i=1; i<=m; i++) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); 79 sort(e+1, e+m+1, cmp); 80 int tot=0; 81 for(int i=1; i<=m; i++){//kruskal 82 int p=find(e[i].u), q=find(e[i].v); 83 if(p!=q){ 84 f[p]=q; 85 add(e[i].u, e[i].v, e[i].w); 86 add(e[i].v, e[i].u, e[i].w); 87 tot++; 88 if(tot==n-1) break; 89 } 90 } 91 for(int i=1; i<=n; i++) if(!vis[i]) dfs(i);//对每个点进行处理 92 scanf("%d",&q); 93 for(int i=1; i<=q; i++){ 94 int x, y; 95 scanf("%d%d",&x,&y); 96 if(find(x)!=find(y)){ 97 printf("-1\n"); 98 continue; 99 } 100 else{ 101 int t=lca(x,y); 102 printf("%d\n",min(ask(x,t),ask(y,t))); 103 } 104 } 105 return 0; 106 }
跑路
这题个人认为比货车运输简单,2^k次很明显是倍增,然后跑最短路
但这里要注意两个点内是否能通过2^k次到达,
如果能的话两点连一条边权为1的边
然后跑最短路
数据范围小到跑Floyd即可
#include<iostream> #include<cstdio> #include<cstring> using namespace std; bool str[60][60][70];//注意开到七十。不然只有四十分qwq int dis[60][60]; int n, m, u, v; int main(){ memset(str, false, sizeof(str)); memset(dis,0x3f,sizeof dis); cin>>n>>m; for(int i=1; i<=m; i++){//预处理 cin>>u>>v; dis[u][v]=1; str[u][v][0]=true; } for(int k=1; k<=64; k++)//连边 for(int i=1; i<=n; i++) for(int t=1; t<=n; t++) for(int j=1; j<=n; j++) if(str[i][t][k-1] && str[t][j][k-1]){ str[i][j][k]=true; dis[i][j]=1; } for(int k=1; k<=n; k++)//Floyd最短路 for(int i=1; i<=n; i++) for(int j=1; j<=n ;j++) dis[i][j]=min(dis[i][k]+dis[k][j],dis[i][j]); printf("%d",dis[1][n]);//输出 return 0; }
开车旅行
这题太难了简直
初始化简直是个毒
思路其实差不多不是很难
开三个数组分别记录倍增后能走到哪个城市,a走多少,b走多少
对于第一个问题直接枚举起点即可
第二个起点直接进行求解
但是初始化太难了不想写不想写不想写
等哪天有心情了再写
时隔这么多天最后还是把这题写了
发现其实好像,没那么难
关于这题的倍增是显而易见的
重点注意细节的处理
吸氧之后快了一倍多
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<set> 5 #include<cstring> 6 #include<cmath> 7 using namespace std; 8 typedef long long ll; 9 const int maxn=100005; 10 ll f[maxn][20], fa[maxn][20], fb[maxn][20]; 11 int na[maxn], nb[maxn]; 12 int n, m, x0, ans, st, x; 13 ll ansa, ansb; 14 struct city{ 15 int id, high; 16 bool operator < (const city & b) const{ 17 return high<b.high; 18 } 19 }a[maxn]; 20 struct node{//用于排序找出最近点和次近点 21 int id, cha; 22 bool operator < (const node & b) const{ 23 if(cha!=b.cha) return cha<b.cha; 24 else return a[id].high<a[b.id].high; 25 } 26 }tmp[5]; 27 set<city> s;//记录每个城市 28 inline void init(int i){//初始化最近和次近 29 set<city> :: iterator it=s.find(a[i]); 30 int cnt=0; 31 if(it!=s.begin()){//不越界的情况下迭代查询 32 --it; 33 tmp[++cnt]=(node){it->id, abs(it->high-a[i].high)}; 34 if(it!=s.begin()){ 35 --it; 36 tmp[++cnt]=(node){it->id, abs(it->high-a[i].high)}; 37 ++it; 38 } 39 ++it; 40 } 41 ++it; 42 if(it!=s.end()){ 43 tmp[++cnt]=(node){it->id, abs(it->high-a[i].high)}; 44 ++it; 45 if(it!=s.end()){ 46 tmp[++cnt]=(node){it->id, abs(it->high-a[i].high)}; 47 } 48 } 49 sort(tmp+1, tmp+1+cnt);//排序 50 nb[i]=tmp[1].id;//最近点 51 if(cnt==1) return; 52 na[i]=tmp[2].id;//次近点 53 return; 54 } 55 inline void cal(int s, int x, ll &sa, ll &sb){ 56 for(int i=19; i>=0; i--){ 57 if(f[s][i] && fa[s][i]+fb[s][i]<=x){ 58 sa+=fa[s][i]; 59 sb+=fb[s][i]; 60 x-=fa[s][i]+fb[s][i]; 61 s=f[s][i]; 62 } 63 } 64 if(!na[s]) return; 65 int tmpans=abs(a[na[s]].high-a[s].high); 66 if(tmpans<=x) sa+=tmpans; 67 } 68 int main(){ 69 scanf("%d",&n); 70 for(int i=1; i<=n; i++){ 71 scanf("%d",&a[i].high); 72 a[i].id=i; 73 } 74 for(int i=n; i>=1; i--){ 75 s.insert(a[i]); 76 if(i!=n) init(i);//初始化 77 } 78 for(int i=1; i<=n; i++){//初始化fa,fb数组 79 int x1=na[i], x2=nb[na[i]]; 80 fa[i][0]=x1?abs(a[x1].high-a[i].high):0; 81 fb[i][0]=x2?abs(a[x2].high-a[x1].high):0; 82 f[i][0]=x2; 83 } 84 for(int j=1; j<20; j++){//倍增处理,后更新的是j所以先枚举j 85 for(int i=1; i<=n; i++){ 86 f[i][j]=f[f[i][j-1]][j-1]; 87 fa[i][j]=fa[i][j-1]+fa[f[i][j-1]][j-1]; 88 fb[i][j]=fb[i][j-1]+fb[f[i][j-1]][j-1]; 89 } 90 } 91 scanf("%d",&x0); 92 ans=0; 93 for(int i=1; i<=n; i++){ 94 ll sa=0ll, sb=0ll; 95 cal(i, x0, sa, sb); 96 if(sb && (!ans || ansa*sb>ansb*sa)){//不断更新最优答案 97 ansa=sa; 98 ansb=sb; 99 ans=i; 100 } 101 } 102 printf("%d\n",ans); 103 scanf("%d",&m); 104 for(int i=1; i<=m; i++){ 105 scanf("%d%d",&st,&x); 106 ll sa=0ll, sb=0ll;//记得初始化 107 cal(st, x, sa, sb); 108 printf("%lld %lld\n", sa, sb); 109 } 110 return 0; 111 }
"Hello World!"