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 }
posted @ 2021-10-13 16:14  B_lank  阅读(79)  评论(0)    收藏  举报