[bzoj4777]Switch Grass

本文介绍了一种利用最小生成树解决特定最短路径问题的方法,并详细展示了通过线段树和multiset数据结构来优化算法的具体实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

结论:最短路径一定是单独的一条边且在最小生成树上,可以用反证法证明。
那么求出最小生成树,对于每一个点建立一棵权值线段树,再对每一个权值线段树上的叶子节点开一个multiset,维护所有儿子中该种颜色的权值(普通节点仍维护区间最小值),答案也需要用multiset维护。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 200005
 4 #define mid (l+r>>1)
 5 multiset<int>ans,s[N*3];
 6 struct ji{
 7     int nex,to,len;
 8 }edge[N<<1];
 9 struct ji2{
10     int x,y,z;
11     bool operator < (const ji2 &k)const{
12         return z<k.z;
13     }
14 }a[N];
15 int E,V,V2,n,m,q,head[N],f[N],r[N],sh[N],id[N*29],ls[N*29],rs[N*29],tr[N*29],c[N];
16 int find(int k){
17     if (k==f[k])return k;
18     return f[k]=find(f[k]);
19 }
20 void add(int x,int y,int z){
21     edge[E].nex=head[x];
22     edge[E].to=y;
23     edge[E].len=z;
24     head[x]=E++;
25 }
26 void update(int &k,int l,int r,int x,int y,int p,int c){
27     if ((!k)&&(y==-1))return;
28     if (!k)k=++V;
29     if (l==r){
30         if (!id[k])id[k]=++V2;
31         if (y!=-1)
32             if (p==1)s[id[k]].insert(y);
33             else s[id[k]].erase(s[id[k]].find(y));
34         if ((c==x)||(!s[id[k]].size()))tr[k]=0x3f3f3f3f;
35         else tr[k]=(*s[id[k]].begin());
36         return;
37     }
38     if (x<=mid)update(ls[k],l,mid,x,y,p,c);
39     else update(rs[k],mid+1,r,x,y,p,c);
40     tr[k]=min(tr[ls[k]],tr[rs[k]]);
41 }
42 void dfs(int k,int fa){
43     f[k]=fa;
44     for(int i=head[k];i!=-1;i=edge[i].nex)
45         if (edge[i].to!=fa){
46             dfs(edge[i].to,k);
47             sh[edge[i].to]=edge[i].len;
48             update(r[k],1,n,c[edge[i].to],edge[i].len,1,c[k]);
49         }
50     ans.insert(tr[r[k]]);
51 }
52 int main(){
53     scanf("%d%d%*d%d",&n,&m,&q);
54     memset(head,-1,sizeof(head));
55     tr[0]=0x3f3f3f3f;
56     for(int i=1;i<=m;i++)scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
57     sort(a+1,a+m+1);
58     for(int i=1;i<=n;i++)f[i]=i;
59     for(int i=1;i<=m;i++)
60         if (find(a[i].x)!=find(a[i].y)){
61             f[find(a[i].x)]=find(a[i].y);
62             add(a[i].x,a[i].y,a[i].z);
63             add(a[i].y,a[i].x,a[i].z);
64         }
65     for(int i=1;i<=n;i++)scanf("%d",&c[i]);
66     dfs(1,0);
67     int x,y;
68     for(int i=1;i<=q;i++){
69         scanf("%d%d",&x,&y);
70         if (f[x]){
71             ans.erase(ans.find(tr[r[f[x]]]));
72             update(r[f[x]],1,n,c[x],sh[x],-1,c[f[x]]);
73             update(r[f[x]],1,n,y,sh[x],1,c[f[x]]);
74             ans.insert(tr[r[f[x]]]);
75         }
76         ans.erase(ans.find(tr[r[x]]));
77         update(r[x],1,n,c[x],-1,1,y);
78         c[x]=y;
79         update(r[x],1,n,c[x],-1,1,c[x]);
80         ans.insert(tr[r[x]]);
81         printf("%d\n",(*ans.begin()));
82     }
83 }
View Code

 

转载于:https://www.cnblogs.com/PYWBKTDA/p/11266526.html

资源下载链接为: https://pan.quark.cn/s/f989b9092fc5 在 Android 应用开发中,开发一款仿 OPPO 手机计算器的应用是极具实践价值的任务,它融合了 UI 设计、事件处理以及数学逻辑等多方面的技术要点。当前的“最新版仿 OPPO 手机计算器--android.rar”压缩包中,提供了该计算器应用的源代码,这为开发者深入学习 Android 编程提供了宝贵的资源。 UI 设计是构建此类计算器应用的基石。OPPO 手机的计算器界面以清晰的布局和良好的用户交互体验著称,其中包括数字键、运算符键以及用于显示结果的区域等关键元素。开发者需借助 Android Studio 中的 XML 布局文件来定义这些界面元素,可选用 LinearLayout、GridLayout 或 ConstraintLayout 等布局管理器,并搭配 Button 控件来实现各个按键功能。同时,还需考虑不同分辨率屏幕和设备尺寸的适配问题,这通常涉及 Density Independent Pixel(dp)单位的应用以及 Android 尺寸资源的合理配置。 事件处理构成了计算器的核心功能。开发者要在每个按钮的点击事件中编写相应的处理代码,通常通过实现 OnClickListener 接口来完成。例如,当用户点击数字键时,相应的值会被添加到显示区域;点击运算符键时,则会保存当前操作数并设定运算类型。而对于等号(=)按钮,需要执行计算操作,这往往需要借助栈数据结构来存储操作数和运算符,并运用算法解析表达式以完成计算。 数学逻辑的实现则是计算器功能的关键体现。在 Android 应用中,开发者可以利用 Java 内置的 Math 类,或者自行设计算法来完成计算任务。基本的加减乘除运算可通过简单的算术操作实现,而像求幂、开方等复杂运算则需调用 Math 类的相关方法。此外
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值