2021.10.13
T1:树
Problem:
现在有一棵树,共 N 个节点。
规定: 根节点为 1 号节点,且每个节点有一个点权。
现在,有 M 个操作需要在树上完成,每次操作为下列三种之一:
1 x a:操作 1,将节点 x 点权增加 a。
2 x a:操作 2,将以节点 x 为根的子树中所有点的权值增加 a。
3 x:操作 3,查询节点 x 到根节点的路径中所有点的点权和
Code:
1 #include<bits/stdc++.h> 2 #define ls (now<<1) 3 #define rs ((now<<1)|1) 4 #define int long long 5 using namespace std; 6 const int maxn=210000; 7 int head[maxn],nxt[maxn],top[maxn],to[maxn]; 8 int f[maxn],w[maxn],id[maxn],val[maxn],siz[maxn]; 9 int n,m,u,v,cnt,tot,dep[maxn],num[maxn],son[maxn]; 10 struct node{ 11 int l,r,sum,add; 12 }tr[maxn<<2]; 13 inline void add(int u,int v){ 14 tot++; 15 to[tot]=v; 16 nxt[tot]=head[u]; 17 head[u]=tot; 18 } 19 inline void dfs(int now,int fa){ 20 dep[now]=dep[fa]+1; 21 siz[now]=1; 22 f[now]=fa; 23 int mx=-1; 24 for(int i=head[now];i;i=nxt[i]){ 25 int y=to[i]; 26 if(y==fa) continue; 27 dfs(y,now); 28 siz[now]+=siz[y]; 29 if(siz[y]>mx){ 30 mx=siz[y]; 31 son[now]=y; 32 } 33 } 34 } 35 inline void bfs(int now,int topn){ 36 id[now]=++cnt; 37 val[cnt]=num[now]; 38 top[now]=topn; 39 if(!son[now]) return ; 40 bfs(son[now],topn); 41 for(int i=head[now];i;i=nxt[i]){ 42 int y=to[i]; 43 if(top[y]) continue; 44 dfs_top(y,y); 45 } 46 } 47 inline void updata(int now){ 48 tr[now].sum=tr[ls].sum+tr[rs].sum; 49 } 50 inline void pushdown(int now){ 51 if(tr[now].add){ 52 int Add=tr[now].add; 53 tr[ls].add+=Add; 54 tr[ls].sum+=Add*(tr[ls].r-tr[ls].l+1); 55 tr[rs].add+=Add; 56 tr[rs].sum+=Add*(tr[rs].r-tr[rs].l+1); 57 tr[now].add=0; 58 } 59 } 60 inline void build(int now,int l,int r){ 61 tr[now].l=l; 62 tr[now].r=r; 63 if(l==r){ 64 tr[now].sum=val[l]; 65 return ; 66 } 67 int mid=(l+r)>>1; 68 build(ls,l,mid); 69 build(rs,mid+1,r); 70 updata(now); 71 } 72 inline void change(int now,int l,int r,int k){ 73 if(tr[now].l>=l&&tr[now].r<=r){ 74 tr[now].sum+=k*(tr[now].r-tr[now].l+1); 75 tr[now].add+=k; 76 return ; 77 } 78 pushdown(now); 79 int mid=(tr[now].l+tr[now].r)>>1; 80 if(l<=mid) change(ls,l,r,k); 81 if(r>mid) change(rs,l,r,k); 82 updata(now); 83 } 84 inline int query(int now,int l,int r){ 85 if(tr[now].l>=l&&tr[now].r<=r){ 86 return tr[now].sum; 87 } 88 pushdown(now); 89 int res=0; 90 int mid=(tr[now].l+tr[now].r)>>1; 91 if(l<=mid) res+=query(ls,l,r); 92 if(r>mid) res+=query(rs,l,r); 93 return res; 94 } 95 inline int tr_query(int a,int b){ 96 int res=0; 97 while(top[a]!=top[b]){ 98 if(dep[top[a]]<dep[top[b]]) swap(a,b); 99 res+=query(1,id[top[a]],id[a]); 100 a=f[top[a]]; 101 } 102 if(dep[a]>dep[b]) swap(a,b); 103 res+=query(1,id[a],id[b]); 104 return res; 105 } 106 signed main(){ 107 scanf("%lld%lld",&n,&m); 108 for(int i=1;i<=n;i++){ 109 scanf("%lld",&num[i]); 110 } 111 for(int i=1;i<n;i++){ 112 scanf("%lld%lld",&u,&v); 113 add(u,v); 114 add(v,u); 115 } 116 dfs(1,1); 117 bfs(1,1); 118 build(1,1,n); 119 int opt,x,a; 120 for(int i=1;i<=m;i++) { 121 scanf("%lld",&opt); 122 if(opt==1){ 123 scanf("%lld%lld",&x,&a); 124 change(1,id[x],id[x],a); 125 } 126 if(opt==2){ 127 scanf("%lld%lld",&x,&a); 128 change(1,id[x],id[x]+siz[x]-1,a); 129 } 130 if(opt==3){ 131 scanf("%lld",&x); 132 cout<<tr_query(1,x)<<"\n"; 133 } 134 } 135 return 0; 136 }
T2:图
Problem:
有一个无向图:共 n 个节点,编号分别为 1~n,同时有 m 条无向边。
不同于他研究的树,图中边和点都有各自的权值,第 i 条边的边权为 wi,第 i 个点的点权为 ci。
从点 s 经过若干条边到点 t 的花费定义为:两点之间经过边的边权之和,加上经过的所有点(包括 s和 t)的点权的最大值。
现在 Makik 将给出 k 次询问,每次给出两个整数 s,t,询问从 s 到 t 的最小花费。
请设计算法帮助 Makik 快速求解答案。
注:图中可能有两点之间存在多条边的情况,但不存在自环。
Code:
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 const ll N=1e6+10; 5 const ll INF=0x7f7f7f7f; 6 struct node{ 7 ll v,id; 8 }v[N]; 9 struct ask{ 10 ll s,t,ans; 11 }b[N]; 12 ll a[300][300],n,m,K,dis[N],vis[N]; 13 inline bool cmp(node x,node y){ 14 return x.v>y.v; 15 } 16 priority_queue<pair<ll,ll>,vector<pair<ll,ll> >,greater<pair<ll,ll> > >q; 17 inline void dij(ll id){ 18 while(q.size()) q.pop(); 19 for(ll i=1;i<=n;++i){ 20 dis[i]=INF; 21 vis[i]=0; 22 } 23 dis[id]=0; 24 q.push(make_pair(0,id)); 25 while(q.size()){ 26 ll x=q.top().second; 27 q.pop(); 28 if(vis[x]) continue; 29 vis[x]=1; 30 for(ll i=1;i<=n;++i){ 31 if(a[x][i]!=INF&&x!=i){ 32 if(dis[i]>dis[x]+a[x][i]){ 33 dis[i]=dis[x]+a[x][i]; 34 q.push(make_pair(dis[i],i)); 35 } 36 } 37 } 38 } 39 } 40 signed main(){ 41 scanf("%d%d%d",&n,&m,&K); 42 for(ll i=1;i<=n;i++){ 43 scanf("%d",&v[i].v); 44 v[i].id=i; 45 } 46 sort(v+1,v+1+n,cmp); 47 for(ll i=1;i<=n;i++){ 48 for(ll j=1;j<=n;j++){ 49 a[i][j]=INF; 50 } 51 } 52 for(ll i=1;i<=m;i++){ 53 ll x,y,w; 54 scanf("%d%d%d",&x,&y,&w); 55 a[x][y]=a[y][x]=min(a[x][y],w); 56 } 57 for(ll i=1;i<=K;++i){ 58 scanf("%d%d",&b[i].s,&b[i].t); 59 b[i].ans=INF; 60 } 61 for(ll i=1;i<=n;++i){ 62 dij(v[i].id); 63 for(ll j=1;j<=K;++j){ 64 b[j].ans=min(b[j].ans,dis[b[j].s]+dis[b[j].t]+v[i].v); 65 } 66 for(ll j=1;j<=n;++j){ 67 a[v[i].id][j]=a[j][v[i].id]=INF; 68 } 69 } 70 for(ll i=1;i<=K;i++){ 71 cout<<b[i].ans<<endl; 72 } 73 return 0; 74 }
T3:地图
Problem:
Makik 有一张详细的城市地图,地图标注了 L 个景区,编号为 1~L。而景区与景区之间建有单向高速通道。
这天,Makik 要去逛景区,他可以任选一个景区开始一天行程,且只能通过单向高速通道进入其他景区。
至少要参观两个景区,游玩最后要回到起始景区。
如果 Makik 参观了第 i 个景区,会获得一个乐趣值 F_i。且参观过得景区不会再获得乐趣值。
对于第 i 条单向高速通道,需要消耗 T_i 的时间,能够从 L1_i 到达 L2_i。
为了简化问题,参观景区不需要花费时间,Makik 想要最终单位时间内获得的乐趣值最大。
请你写个程序,帮 Makik 计算一下他能得到的最大平均乐趣值。
Code:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e5+5; 4 const int inf=0x3f3f3f3f; 5 const double eps=1e-4; 6 int n,m,a[1005],tim[1005]; 7 double l,r,ans,dis[1005]; 8 bool vis[1005]; 9 struct edge{ 10 int u,v,w; 11 }e[5005]; 12 struct EDGE{ 13 int to; 14 double val; 15 EDGE *nxt; 16 }*head[maxn]; 17 inline void add(int x,int y,double z){ 18 head[x]=new(EDGE){y,z,head[x]}; 19 } 20 inline bool spfa(){ 21 memset(vis,0,sizeof(vis)); 22 memset(tim,0,sizeof(tim)); 23 for(int i=1;i<=n;++i) dis[i]=-inf; 24 queue<int>q; 25 q.push(1); 26 dis[1]=0; 27 while(q.size()){ 28 int now=q.front(); 29 q.pop(); 30 vis[now]=0; 31 #define to i->to 32 #define val i->val 33 for(EDGE *i=head[now];i;i=i->nxt){ 34 if(dis[to]<dis[now]+val){ 35 dis[to]=dis[now]+val; 36 if(!vis[to]){ 37 q.push(to); 38 vis[to]=1; 39 } 40 tim[to]++; 41 if(tim[to]>n) return true; 42 } 43 } 44 } 45 return false; 46 } 47 inline bool judge(double x){ 48 memset(head,0,sizeof(head)); 49 for(int i=1;i<=m;++i){ 50 add(e[i].u,e[i].v,(double)a[e[i].v]-x*e[i].w); 51 } 52 return spfa(); 53 } 54 signed main(){ 55 scanf("%d%d",&n,&m); 56 for(int i=1;i<=n;++i){ 57 scanf("%d",&a[i]); 58 r+=a[i]; 59 } 60 for(int i=1;i<=m;++i){ 61 scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); 62 } 63 while(r-l>=eps){ 64 double mid=(l+r)/2.0; 65 if(judge(mid)){ 66 ans=mid; 67 l=mid; 68 } 69 else r=mid; 70 } 71 printf("%.2f\n",ans); 72 return 0; 73 }

浙公网安备 33010602011771号