zoukankan      html  css  js  c++  java
  • CF487E Tourists

    题面

    题目大意:

    给定一张 $ n $ 个点 (m) 条边的无向图,每个点都有权值 (w_i),要处理 (q)​ 个操作

    • 将某个点的权值修改
    • 询问两点间路径中点的最小权值

    $ 1leq m,q,n leq 10^5, 1leq w_ileq 10^9$

    solution

    知识点:圆方树

    什么是圆方树

    无向图,对于每个点双(任意两点都有两条路径可以到达),建一个方点,让方点和它对应的点双中的圆点连边。

    如何构建圆方树?

    (tarjan)​ 求出每个点双,新建一个节点,连边就好了

    因为是求路径上的最小值,可以考虑把方点的权值赋为所在点双中所有点权值的最小值,因为每次修改都要比较方点周围的所有点,所以遇见菊花图就很容易被卡成 (O(nq))

    因为圆方树是一棵树,所以考虑树的性质,对于每个方点权值赋为它所有儿子权值的最小值,用 (multiset) 维护就好了

    multiset

    code

    /*
    work by:Ariel_
    建立圆方树,方点开 multiset 存周围点的最小值,然后树剖求链上最小值就好了 
    */
    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<set>
    #define lson rt << 1
    #define rson rt << 1|1
    #define ll long long
    #define rg register
    using namespace std;
    const int MAXN = 2e5 + 5;
    const int INF = 0x3f3f3f3f;
    int read(){
        int x = 0,f = 1; char c = getchar();
        while(c < '0'||c > '9') {if(c == '-') f = -1; c = getchar();}
        while(c >= '0' && c <= '9') {x = x*10 + c - '0'; c = getchar();}
        return x*f;
    }
    int n, m, q, w[MAXN], fcnt;
    int fa[MAXN], dep[MAXN], siz[MAXN], son[MAXN], top[MAXN], id[MAXN], o, val[MAXN];
    multiset<int> S[200010];
    struct edge {int v, nxt;}e[MAXN << 1], E[MAXN << 1];
    int Head[MAXN], head[MAXN], Ecnt, ecnt;
    void Add_edge(int u, int v) {
       E[++Ecnt] = (edge){v, Head[u]};
       Head[u] = Ecnt;
    } 
    void add_edge(int u, int v)  {
       e[++ecnt] = (edge) {v, head[u]};
       head[u] = ecnt;
    }
    namespace Seg{ 
       struct Tree {
         int l, r, minn;
       }tree[MAXN << 3];
       void push_up(int rt) {
       	  tree[rt].minn = min(tree[lson].minn, tree[rson].minn);
       }
       void build(int rt, int l, int r) {
         tree[rt].l = l, tree[rt].r = r;
         if (l == r) {
         	tree[rt].minn = w[val[l]];
         	return ;
    	 }   
    	 int mid = (l + r) >> 1;
    	 build(lson, l, mid), build(rson, mid + 1, r);
    	 push_up(rt);
       }
       void update(int rt, int l, int r, int p, int k) {
       	  if (tree[rt].l == tree[rt].r) {
       	      tree[rt].minn = k;
       	      return ;
    	   }
    	  int mid = (l + r) >> 1;
    	  if (p <= mid) update(lson, l, mid, p, k);
    	  else update(rson, mid + 1, r, p, k);
    	  push_up(rt);
       }
       int query(int rt, int l, int r, int L, int R) {
       	  if (L <= l && r <= R) return tree[rt].minn;
       	  int ret = INF;
       	  int mid = (l + r) >> 1;
       	  if (L <= mid) ret = min(ret, query(lson, l, mid, L, R));
       	  if (R > mid) ret = min(ret, query(rson, mid + 1, r, L, R));
       	  return ret;
       }
    }
    using namespace Seg;
    
    namespace Cut{ 
       void dfs(int x, int f) {
       	fa[x] = f, dep[x] = dep[f] + 1, siz[x] = 1; 
    	for (int i = head[x]; i; i = e[i].nxt) {
       	        int v = e[i].v;
    			if (v == f) continue;
    			dfs(v, x);
    		  siz[x] += siz[v];
    		  if (siz[v] > siz[son[x]]) son[x] = v;	
    	   }
       }
       void dfs2(int x, int tp) {
       	 top[x] = tp, id[x] = ++o, val[o] = x;
    	 if (son[x]) dfs2(son[x], tp);
       	 for (int i = head[x]; i; i = e[i].nxt) {
       	 	    int v = e[i].v;
       	 	    if(v == fa[x] || v == son[x]) continue;
       	 	    dfs2(v, v);
    		}
       } 
       int Query(int x, int y) {//查询路径最小值 
       	  int ret = INF;
       	  while(top[x] != top[y]) {
       	     if (dep[top[x]] < dep[top[y]]) swap(x, y);
    		 ret = min(ret, query(1, 1, fcnt, id[top[x]], id[x]));
    		 x = fa[top[x]]; 	
    	   }
    	  if (dep[x] > dep[y]) swap(x, y);
    	  ret = min(ret, query(1, 1, fcnt, id[x], id[y]));
    	  if (x > n) ret = min(ret, w[fa[x]]);
    	  return ret;
       }
    }
    
    
    using namespace Cut;
    
    int dfn[MAXN], low[MAXN], Stack[MAXN], tot, cnt;
    void Tarjan(int x) {
       dfn[x] = low[x] = ++tot;
       Stack[++cnt] = x;
       for (int i = Head[x]; i; i = E[i].nxt) {
       	     int v = E[i].v;
       	     if (!dfn[v]) {
       	        Tarjan(v);
    			low[x] = min(low[x], low[v]);
    		    if (low[v] == dfn[x]) {
    		       fcnt++;//方点的个数
    			   for (int j = 0; j != v; cnt--) {
    			   	   j = Stack[cnt];
    			   	   add_edge(fcnt, j), add_edge(j, fcnt);//方点和原点建边 
    			   } 
    			   add_edge(fcnt, x), add_edge(x, fcnt);//把最后一个点加上 
    			}
    		 }
    		else low[x] = min(low[x], dfn[v]);
       }
    }
    char s[5];
    int main(){
       n = read(), m = read(), q = read();
       for (int i = 1; i <= n; i++) w[i] = read();
       for (int i = 1, u, v; i <= m; i++) {
       	   u = read(), v = read();
       	   Add_edge(u, v), Add_edge(v, u); 
       }
       fcnt = n;
       Tarjan(1), dfs(1, 0), dfs2(1, 1);
       for (int i = 1; i <= n; i++)  
          if (fa[i]) S[fa[i]].insert(w[i]);//存每个点的儿子 
       for (int i = n + 1; i <= fcnt; i++)  w[i] = *S[i].begin();//每个方点存周围点的最小值 
       build(1, 1, fcnt);
       while(q--) {
       	  scanf("%s", s + 1);
       	  if (s[1] == 'C') {
       	    int x = read(), y = read();
       	    update(1, 1, fcnt, id[x], y);
       	    if (fa[x]) {
       	       S[fa[x]].erase(S[fa[x]].lower_bound(w[x]));
    		   S[fa[x]].insert(y);
    		   if (w[fa[x]] != *S[fa[x]].begin()) {
    		   	    w[fa[x]] = *S[fa[x]].begin();
    		   	    update(1, 1, fcnt, id[fa[x]], w[fa[x]]);
    		   }	
    		}
    		w[x] = y;
    	  }
    	  else if(s[1] == 'A'){
    	  	 int x = read(), y = read();
    	  	 printf("%d
    ", Query(x, y));
    	  }
       }
       puts(""); 
       return 0;
    }
    
    
  • 相关阅读:
    微信 token ticket jsapi_ticket access_token 获取 getAccessToken get_jsapi_ticket方法
    PHP 日志 记录 函数 支持 数组 对象 新浪 sae 环境 去掉 空格 换行 格式化 输出 数组转字符串
    原生 原始 PHP连接MySQL 代码 参考mysqli pdo
    PHP 数字金额转换成中文大写金额的函数 数字转中文
    使用PHPMailer发送带附件并支持HTML内容的邮件
    设置输出编码格式 header 重定向 执行时间 set_time_limit 错误 报告 级别 error_reporting
    html5 bootstrap pannel table 协议 公告 声明 文书 模板
    指向指针的指针
    二级指针
    c语言:当指针成为参数后
  • 原文地址:https://www.cnblogs.com/Arielzz/p/15060132.html
Copyright © 2011-2022 走看看