P3665 [USACO17OPEN]Switch Grass

题目描述

  N个点M条边的无向图,每个点有一个初始颜色,每次改变一个点的颜色,求改变后整张图上颜色不同的点之间的距离最小值。

思路

考虑整张图的距离最小值一定是一条边,而不可能是一条路径,那么显然这条边一定在MST上,于是我们考虑怎样维护这棵MST。

  • 首先我们用kruskal建出MST,然后转化为有根树,把边权下放为点权,只维护每个点和它儿子的信息。
  • 我们对每个节点所有儿子的颜色开一个multiset,维护每个点的儿子中每种颜色对应的最小边权(此时下放为点权),为了节省空间我们用map映射把颜色那维去掉,即$CLS_{u,col}}
  • 接着我们再对每个点开一个multiset维护每个点儿子中所有颜色对应的最小边权的最小值(就是上一个multiset维护的东西的最小值)。即$best[u]=min\{CLS_{u,c} \}_{C \in all colors }$
  • 最后我们对全局开一棵线段树,维护每个点的best中和它颜色不同的点的最小值,便于修改和查询。
  • 于是每个点的答案不是$*best.begin()就是*(best.begin()+1)$,如果儿子中点权最小的儿子和父亲颜色相同,那么显然是次小的(或者无解)。
  • 怎样修改,考虑我们修改u时,先把$u$从父亲节点的信息中删去,再加回来。这里的方法是:先把$best$中对应的$col[u]$的信息删掉,然后把当前点所在的$cls[f[u]][col]$中找到并删去当前点,重新排序后再加入$best$中;接着把u的信息加入到$cls[f[u]][B]$(B为修改后颜色)中,同样先删去$best$中B的信息删掉,然后把u的信息加入$cls[f[u][B]$中,重新排序后再加入$best$中。
#include<bits/stdc++.h>
#define I inline
#define fi first
#define se second
#define mp make_pair
#define ls (now<<1)
#define rs (now<<1|1)
#define smid (l+r>>1)
using namespace std;
typedef map<int,multiset<int> >::iterator itcls;
//神仙typedef,这里是map的指针,it->fi是前面的集合,it->se是后面映射的集合
typedef set<pair<int,int> >::iterator itset;//it是一个pair型的东西
const int N=2000010;
const int inf=2147483647;
I int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}

struct EDGE
{
    int u,v,w;
}e[N];
struct node
{
    int to,nxt,w;
}g[N<<1];
int head[N],cnt;
int n,m,k,q;
int col[N],w[N],f[N];

int mi[N<<2];
I void pushup(int now){mi[now]=min(mi[ls],mi[rs]);}

I void bt(int now,int l,int r)
{
    mi[now]=inf;
    if(l==r)return;
    bt(ls,l,smid);bt(rs,smid+1,r);
    pushup(now);
}

I void modify(int now,int l,int r,int pos,int val)
{
    if(l==r){mi[now]=val;return;}
    if(pos<=smid)modify(ls,l,smid,pos,val);
    else modify(rs,smid+1,r,pos,val);
    pushup(now);
}

I bool cmp(EDGE a,EDGE b){return a.w<b.w;}

I void add(int u,int v,int w)
{
    g[++cnt].nxt=head[u];
    g[cnt].to=v;g[cnt].w=w;
    head[u]=cnt;
}

struct DSU
{
    int f[N];
    I void init(int x){for(int i=1;i<=x;i++)f[i]=i;}
    I int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);}
}dsu;

I void kruskal()
{
    sort(e+1,e+1+m,cmp);
    int tot=0;cnt=0;
    dsu.init(n);
    for(int i=1;i<=m;i++)
    {
        int u=e[i].u,v=e[i].v,w=e[i].w;
        if(dsu.getf(u)!=dsu.getf(v))
        {
            dsu.f[dsu.getf(u)]=dsu.getf(v);
            tot++;add(u,v,w);add(v,u,w);
            if(tot==n-1)return;
        }
    }
}

map<int,multiset<int> >cls[N];
multiset<pair<int,int> >best[N];

I void dfs(int u,int fa)
{
    f[u]=fa;
    for(int i=head[u];i;i=g[i].nxt)
    {
        int v=g[i].to;
        if(v==fa)continue;
        w[v]=g[i].w;cls[u][col[v]].insert(w[v]);
        dfs(v,u);
    }
    for(itcls it=cls[u].begin();it!=cls[u].end();it++)
    best[u].insert(mp(*((it->se).begin()),it->fi));
    //真·STL,(it维护的是map的指针,it->se是映射的multiset,it->fi是颜色
    if(best[u].empty())return;

    itset it=best[u].begin();
    if((it->se)!=col[u])modify(1,1,n,u,it->fi);
    else
    {
        it++;if(it==best[u].end())modify(1,1,n,u,inf);
        else modify(1,1,n,u,it->fi);
    }
}

int main()
{
    n=read();m=read();k=read();q=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read(),z=read();
        e[++cnt]=(EDGE){x,y,z};
    }
    for(int i=1;i<=n;i++)col[i]=read();
    kruskal();bt(1,1,n);dfs(1,0);
    while(q--)
    {
        int u=read(),c2=read();
        if(f[u])
        {
            int c1=col[u];
            best[f[u]].erase(best[f[u]].find(mp(*cls[f[u]][c1].begin(),c1)));
            cls[f[u]][c1].erase(cls[f[u]][c1].find(w[u]));
            if(cls[f[u]][c1].empty())cls[f[u]][c1].insert(inf);
            best[f[u]].insert(mp(*cls[f[u]][c1].begin(),c1));

            if(!cls[f[u]][c2].empty())
            best[f[u]].erase(best[f[u]].find(mp(*cls[f[u]][c2].begin(),c2)));
            cls[f[u]][c2].insert(w[u]);
            best[f[u]].insert(mp(*cls[f[u]][c2].begin(),c2));
            itset it=best[f[u]].begin();
            if((it->se)!=col[f[u]])modify(1,1,n,f[u],it->fi);
            else
            {
                it++;if(it==best[f[u]].end())modify(1,1,n,f[u],inf);
                else modify(1,1,n,f[u],it->fi);
            }
        }
        if(!best[u].empty())
        {
            itset it=best[u].begin();
            if((it->se)!=c2)modify(1,1,n,u,it->fi);
            else
            {
                it++;if(it==best[u].end())modify(1,1,n,u,inf);
                else modify(1,1,n,u,it->fi);
            }
        }
        col[u]=c2;
        printf("%d\n",mi[1]);
    }
}

原文地址:https://www.cnblogs.com/THRANDUil/p/11823874.html

时间: 2024-08-30 17:12:48

P3665 [USACO17OPEN]Switch Grass的相关文章

usaco 2017 US Open

A:Modern Art 给定一个n*n的网格,有n*n个颜色,每种颜色按一定顺序覆盖了一个矩形.给定末状态,求有几种颜色可能是第一个填上去的.n<=1000 题解:二维查分+前缀和起来,然后就可以快速求得每个点被覆盖了多少次啦.复杂度n^2 #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; inline int

选择结构if语句和switch语句的区别

1.选择结构if语句格式及其使用 A:if语句的格式: if(比较表达式1) { 语句体1; }else if(比较表达式2) { 语句体2; }else if(比较表达式3) { 语句体3; } ... else { 语句体n+1; } B:执行流程: 首先计算比较表达式1看其返回值是true还是false, 如果是true,就执行语句体1,if语句结束. 如果是false,接着计算比较表达式2看其返回值是true还是false, 如果是true,就执行语句体2,if语句结束. 如果是fals

java基础之switch

switch 语句由一个控制表达式和多个case标签组成. switch 控制表达式支持的类型有byte.short.char.int.enum(Java 5).String(Java 7). switch-case语句完全可以与if-else语句互转,但通常来说,switch-case语句执行效率要高. default在当前switch找不到匹配的case时执行.default并不是必须的. 一旦case匹配,就会顺序执行后面的程序代码,而不管后面的case是否匹配,直到遇见break. sw

蓝鸥Unity开发基础——Switch语句学习笔记

一.Switch语法 属于多分支语句,通过判断表达式的值,来决定执行哪个分支 Break用于结束某个case,然后执行switch之外的语句 Switch-开关:case-情况开关决定发生的情况 二.Switch基本语法 Switch(表达式){ Case 值1: 语句1 Break: Case 值2: 语句2 Break: -- Case 值n: 语句n Break: Default: 语句 Break: } 三.注意事项 整个defaul语句都可以舍掉,default语句最多只能由一个 Sw

JAVA之Switch语句

switch case语句是用来判断case后面的表达式是否与switch的表达式一致,符合的话就执行case语句,不符合则break跳出.而default是当没有符合case的条件下执行的(它不用break跳出的),defaul相当于”第三种情况“,在switch case语句中也可以不使用. public class SwitchDemo { public static void main(String[] args) { // TODO Auto-generated method stub

switch case 与 if

case 在编程中偶尔使用到switch case语句,对于case语句的处理,出现了两种错误,现总结如下: 1 case后必须是常量,不能使用‘<’或‘>’这种逻辑运算 2 case后如果是‘||’或者‘&&’逻辑运算,则实际是1或者0 #include <iostream> using namespace std; int main(int argc, char * argv[]) { int i; cin>>i; switch(i) { case

数据字典+匿名委托模拟switch/case功能

基本思想每个case的选择值作为键,处理过程做成函数委托存放进数据字典.用的时候根据之调用.下面上代码 封装的FuncSwitcher类 using System; using System.Collections.Generic; namespace Test {     class FuncSwitcher<T>     {         int count;         Dictionary<T, Delegate> FuncGather;         Delega

Swift语言中的switch语句的妙用

Swift中的switch语句的类另用法: // 强大的switch语句 var a:Int = 100 switch a { case a where a < 0: println("Negative") case a where a == 0: println("Zero") case a where a > 0: println("Positive") default: println("Unknow") }

uva 10382 Watering Grass(贪心)

uva 10382 Watering Grass n sprinklers are installed in a horizontal strip of grass l meters long and w meters wide. Each sprinkler is installed at the horizontal center line of the strip. For each sprinkler we are given its position as the distance f