标签:最长路 之间 区间更新 魔法 open sed ini iostream 传递
A.小希的迷宫(并查集)
题意:
上次Gardon的迷宫城堡小希玩了很久(见Problem B),现在她也想设计一个迷宫让Gardon来走。但是她设计迷宫的思路不一样,首先她认为所有的通道都应该是双向连通的,就是说如果有一个通道连通了房间A和B,那么既可以通过它从房间A走到房间B,也可以通过它从房间B走到房间A,为了提高难度,小希希望任意两个房间有且仅有一条路径可以相通(除非走了回头路)。小希现在把她的设计图给你,让你帮忙判断她的设计图是否符合她的设计思路。
题解:
即判断一个图是否成环,用并查集实现,当前要合并的两个节点属于一个集合时,输出NO
#include<cstdio> #include<algorithm> #include<cstring> #include<vector> #include<unordered_map> using namespace std; const int maxn=1e6+14; const int inf=1e9; int father[maxn],isRoot[maxn],flag,visit[maxn]; void init () { for (int i=0;i<maxn;i++) father[i]=i; flag=0; fill (isRoot,isRoot+maxn,0); fill (visit,visit+maxn,0); } int findfather (int x) { int a=x; while (x!=father[x]) x=father[x]; while (a!=father[a]) { int z=a; a=father[a]; father[z]=x; } return x; } void Union (int a,int b) { int faA=findfather(a); int faB=findfather(b); if (faA!=faB) father[faA]=faB; } int main () { while (1) { int flag=0,x,y; init (); while (1) { scanf ("%d %d",&x,&y); if (x==0&&y==0) break; if (x==-1&&y==-1) return 0; if (findfather(x)==findfather(y)) flag++; visit[x]=1;visit[y]=1; Union (x,y); } for (int i=0;i<maxn;i++) if (visit[i]) isRoot[findfather(i)]++; int ans=0; for (int i=0;i<maxn;i++) if (isRoot[i]&&visit[i]) ans++; if (ans>1) flag++; //printf ("%d\n",ans); if (flag==0) printf ("Yes\n"); else printf ("No\n"); } return 0; }
B.Eugene and an array(前缀和)
题意:
一个区间中若有一段连续的子区间的区间和为0,则这个区间是 不好的 ,给定一个区间求这个区间内有多少 好 的子区间
题解:
从左往右遍历,如果当前的前缀和在map里出现过,说明这个前缀和之前的位置到当前位置的序列是不合法的,统计到之前位置的序列就好。时间复杂度O(N)
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=2e5+100; int a[maxn]; map<ll,ll> pos; int main () { int N; scanf("%d",&N); pos[0]=1; ll sum=0,ans=0,Max=1; for (int i=2;i<=N+1;i++) { scanf("%d",&a[i]); sum+=a[i]; if (pos[sum]!=0) Max=max(Max,pos[sum]+1); ans+=i-Max; pos[sum]=i; } printf("%lld\n",ans); }
C.Circles of Monsters(前缀和)
题意:
你在玩另一个电脑游戏,现在你必须杀死n个怪物。这些怪物站成一个圈,顺时针从1到n编号。最初,第i个怪物有ai生命值。
你可以射击怪物杀死他们。每次射击只需要一颗子弹,并且使目标怪物的生命值降低1(对其造成1点伤害)。此外,当某个怪物i的生命值变为0或小于0时,它会死亡并爆炸,对下一个怪物造成bi伤害(如果i如果下一个怪物已经死了,那么什么也不会发生。如果爆炸杀死了下一个怪物,它也会爆炸,在爆炸后破坏怪物并可能引发另一个爆炸,等等。
你必须计算你必须发射的子弹的最小数量,以杀死所有的n个怪物在圆。
题解:
给出一圈怪兽,每个怪兽都有生命值和攻击力,当你击杀一个怪兽,这个怪兽会对下一个怪兽造成等于它攻击力的伤害,依次传递,当无法击杀怪兽或者碰到已经死亡的怪兽就停止。询问至少要消灭多少生命值的怪兽才能击杀所有怪兽。
做法是开一个两倍的数组模拟循环,然后开一个C数组,表示以每个怪兽为起点发动攻击,所消耗的子弹数量,可以推导状态转移方程:
if (a[i]<=b[i-1]) c[i]=c[i-1]; else c[i]=c[i-1]+a[i]-b[i-1]; if (i>=N) ans=min(ans,c[i]-c[i-N+1]+a[i-N+1]);
完整代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=2e5+100; int a[maxn]; map<ll,ll> pos; int main () { int N; scanf("%d",&N); pos[0]=1; ll sum=0,ans=0,Max=1; for (int i=2;i<=N+1;i++) { scanf("%d",&a[i]); sum+=a[i]; if (pos[sum]!=0) Max=max(Max,pos[sum]+1); ans+=i-Max; pos[sum]=i; } printf("%lld\n",ans); }
D.National Road System(最小生成树)
题意:
给出n个城市的x,y坐标以及每个城市的人数, 这些城市的主人想建造最小生成树,这时候有个会魔法的道士说, 我可以让一条路权值为0, 求A/B的最大值, 其中A是权值为0的道路连接的城市的人数之和, B是最小生成树的权值。
题解:
再遍历每一对顶点,如果该顶点之间的边属于最小生成树,则剪掉这对顶点在最小生成树里的最长路径
否则直接剪掉连接这对顶点的边~
用prim算法求最小生成树最长路径的模板~
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<cmath> using namespace std; const int maxn=1010; const int inf=1e9; double g[maxn][maxn],d[maxn],w[maxn]; int visit[maxn],N,S,P,pre[maxn]; double len[maxn][maxn];//表示点i,j在最小生成树上的最长直径 double c[maxn][maxn]; void init () { for (int i=0;i<maxn;i++) for (int j=0;j<maxn;j++) g[i][j]=inf,c[i][j]=0,len[i][j]=0; } double prim (int s) { fill(d,d+maxn,inf); fill(visit,visit+maxn,0); for (int i=1;i<=N;i++) pre[i]=i; d[s]=0; double ans=0; for (int i=1;i<=N;i++) { int u=-1,min=inf; for (int j=1;j<=N;j++) if (!visit[j]&&d[j]<min) { u=j;min=d[j]; } if (u==-1) return -1; visit[u]=1; ans+=d[u]; if (pre[u]!=u) c[u][pre[u]]=c[pre[u]][u]=1; for (int v=1;v<=N;v++) if (!visit[v]&&g[u][v]!=inf&&g[u][v]<d[v]) { d[v]=g[u][v]; pre[v]=u; } for (int v=1;v<=N;v++) if (visit[v]&&u!=v) len[u][v]=len[v][u]=max(len[pre[u]][v],d[u]); } return ans; } struct node { double x,y; }Node[maxn]; int main () { int T; scanf("%d",&T); while (T--) { scanf("%d",&N); for (int i=1;i<=N;i++) scanf("%lf%lf%lf",&Node[i].x,&Node[i].y,&w[i]); init(); for (int i=1;i<=N;i++) for (int j=i+1;j<=N;j++) g[i][j]=g[j][i]=sqrt((Node[i].x-Node[j].x)*(Node[i].x-Node[j].x)+ (Node[i].y-Node[j].y)*(Node[i].y-Node[j].y)); double mst=prim(1); double Max=0; for (int i=1;i<=N;i++) for (int j=i+1;j<=N;j++) if (!c[i][j]) Max=max(Max,(w[i]+w[j])/(mst-len[i][j])); else Max=max(Max,(w[i]+w[j])/(mst-g[i][j])); printf("%.2f\n",Max); } }
E.Just a Hook(线段树)
题意:
初始的时候,整个序列都是1,接下来,每次输入l,r,x。表示将l到r之间修改为x且x只会是1、2、3,最后问你序列总和。
题解:
线段树+延迟标记,老模板题了
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=1e6+100; struct node { int l,r,sum,lazy; }segTree[maxn*4]; int N,M; void build (int i,int l,int r) { segTree[i].l=l; segTree[i].r=r; segTree[i].lazy=0; if (l==r) { segTree[i].sum=1; return; } int mid=(l+r)>>1; build(i<<1,l,mid); build(i<<1|1,mid+1,r); segTree[i].sum=segTree[i<<1].sum+segTree[i<<1|1].sum; } void spread (int i) { if (segTree[i].lazy) { segTree[i<<1].sum=segTree[i].lazy*(segTree[i<<1].r-segTree[i<<1].l+1); segTree[i<<1|1].sum=segTree[i].lazy*(segTree[i<<1|1].r-segTree[i<<1|1].l+1); segTree[i<<1].lazy=segTree[i].lazy; segTree[i<<1|1].lazy=segTree[i].lazy; segTree[i].lazy=0; } } void update (int i,int l,int r,int val) { //区间更新 if (l<=segTree[i].l&&segTree[i].r<=r) { segTree[i].sum=val*(segTree[i].r-segTree[i].l+1); segTree[i].lazy=val; return; } spread(i); int mid=(segTree[i].l+segTree[i].r)>>1; if (l<=mid) update(i<<1,l,r,val); if (r>mid) update(i<<1|1,l,r,val); segTree[i].sum=segTree[i<<1].sum+segTree[i<<1|1].sum; } int query (int i,int l,int r) { if (l<=segTree[i].l&&r>=segTree[i].r) return segTree[i].sum; spread(i); int mid=(segTree[i].l+segTree[i].r)>>1; int ans=0; if (l<=mid) ans+=query(i<<1,l,r); if (r>mid) ans+=query(i<<1|1,l,r); return ans; } int T; int main () { scanf("%d",&T); for (int k=1;k<=T;k++) { scanf("%d%d",&N,&M); build(1,1,N); for (int i=1;i<=M;i++) { int l,r,x; scanf("%d%d%d",&l,&r,&x); update(1,l,r,x); } printf("Case %d: The total value of the hook is %d.\n",k,query(1,1,N)); } }
F.I Hate It(线段树)
题意:
很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。
这让很多学生很反感。
不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。
题解:
线段树单点修改+区间查询,属于简单题
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<string> using namespace std; const int maxn=2e5+100; struct node { int l,r,sum,lazy; }segTree[maxn*4]; int a[maxn]; int N,M; void build (int i,int l,int r) { segTree[i].l=l; segTree[i].r=r; segTree[i].lazy=0; if (l==r) { segTree[i].sum=a[l]; return; } int mid=(l+r)>>1; build(i<<1,l,mid); build(i<<1|1,mid+1,r); segTree[i].sum=max(segTree[i<<1].sum,segTree[i<<1|1].sum); } void update (int i,int x,int y) { if (segTree[i].l==x&&segTree[i].r==x) { segTree[i].sum=y; return; } int mid=(segTree[i].l+segTree[i].r)>>1; if (x<=mid) update(i<<1,x,y); else update(i<<1|1,x,y); segTree[i].sum=max(segTree[i<<1].sum,segTree[i<<1|1].sum); } int query (int i,int l,int r) { if (segTree[i].l>=l&&segTree[i].r<=r) { return segTree[i].sum; } int mid=(segTree[i].l+segTree[i].r)>>1; int Max=-1; if (l<=mid) Max=max(Max,query(i<<1,l,r)); if (r>mid) Max=max(Max,query(i<<1|1,l,r)); return Max; } int main () { while (~scanf("%d%d",&N,&M)) { for (int i=1;i<=N;i++) scanf("%d",&a[i]); build(1,1,N); string s; int x,y; for (int i=1;i<=M;i++) { cin>>s>>x>>y; if (s=="Q") { printf("%d\n",query(1,x,y)); } else { update(1,x,y); } } } }
G.BillBoard(线段树)
题意:
题解:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=1e6+15; struct node { int l,r,sum; }segTree[maxn*4]; int h,w,n; void build (int i,int l,int r) { segTree[i].l=l; segTree[i].r=r; segTree[i].sum=w; if (l==r) return; int mid=(l+r)>>1; build(i<<1,l,mid); build(i<<1|1,mid+1,r); segTree[i].sum=max(segTree[i<<1].sum,segTree[i<<1|1].sum); } int query (int i,int x) { if (segTree[i].l==segTree[i].r) { segTree[i].sum-=x; return segTree[i].l; } int mid=(segTree[i].l+segTree[i].r)>>1; int ans; if (segTree[i<<1].sum>=x) ans=query(i<<1,x); else ans=query(i<<1|1,x); segTree[i].sum=max(segTree[i<<1].sum,segTree[i<<1|1].sum); return ans; } int main () { while (~scanf("%d%d%d",&h,&w,&n)) { h=min(h,n); build(1,1,h); int x; while (n--) { scanf("%d",&x); if (segTree[1].sum<x) printf("-1\n"); else printf("%d\n",query(1,x)); } } }
标签:最长路 之间 区间更新 魔法 open sed ini iostream 传递
原文地址:https://www.cnblogs.com/algorithmgroup/p/12723165.html