zoukankan      html  css  js  c++  java
  • WC2010 重建计划

    传送门

    这道题……如bin哥所说,算法不难想,但是很难写,而且还有些卡常……

    其实这题我做的很奇怪……就是大致的算法都是对的但是我和别人的实现方法一点都不一样……(我怎么总是这么奇怪的做题)

    因为要求平均值,所以就可以想到01分数规划,二分答案,然后要求的是树上的一条路径,那就是点分治咯。想到这就已经是两个(log)了,如果要是三个(log)估计死活过不去。再考虑考虑怎么合并,合并的复杂度还必须是(O(n))的。借鉴着上一道题的做法,很自然能想到用(dis_x)表示重心的每棵子树中长度为(x)的路径的长度,只需要保留最长的那一条就行了,这个直接dfs一遍就行(但是好像所有人都用bfs?)之后就是咋合并了。

    (tmp_i)表示前s-1棵子树之内长为i的路径最大值。发现每一条路径只能和长度在一段区间之内的路径合并更新答案,然后这个区间随着枚举的值左右端点都递增,那么使用单调队列维护一下就好。

    结果写的时候感觉自己像没学过单调队列……写错两次。这里我的实现方法是,从小到大枚举当前子树的链长,先倒着把长度为(l+1~u)的链放在单调队列里,然后每次枚举的时候把长度为(l-i)的压入单调队列,之后就都一样了。最后要更新答案,并且把数组还原回(-INF),方便之后递归使用。

    别人都是对于每一个重心二分答案,然后我是……直接在外面二分答案,然后把最长链求完更新。所以一开始我还建了点分树。程序一开始交上去T了8个点……后来改小了二分的次数(其实35次就够用了),在合并过程中,我记录子树链长最大值(maxdep),这样单调队列一开始只需要压入(l+1~maxdep)的值就可以了。这样其实可以过了,不过还是挺慢的……

    最后加了个重要剪枝,子树(size)小于(l)的不建点分树,然后还优化了一下其他的小地方,比原来快了很多。发现自己和hzwer跑的差不多快,干脆不卡了,就这样吧。

    看一下代码。

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #define rep(i,a,n) for(register int i = a;i <= n;i++)
    #define per(i,n,a) for(register int i = n;i >= a;i--)
    #define enter putchar('
    ')
    #define pr pair<int,int>
    #define mp make_pair
    #define fi first
    #define sc second
    using namespace std;
    typedef long long ll;
    const int M = 100005;
    const int N = 10000005;
    const double INF = 1e13;
     
    int read()
    {
       int ans = 0,op = 1;
       char ch = getchar();
       while(ch < '0' || ch > '9')
       {
          if(ch == '-') op = -1;
          ch = getchar();
       }
       while(ch >='0' && ch <= '9')
       {
          ans *= 10;
          ans += ch - '0';
          ch = getchar();
       }
       return ans * op;
    }
    
    struct edge
    {
       int next,to;
       double d,v;
    }e[M<<1],e1[M<<1];
    
    int n,size[M],hson[M],G,sum,l,u,x,y,Head,tail,q[M],head[M],ecnt,fq[M],st;
    int head1[M],ecnt1,dep[M],mdep,maxdep;
    double z,ans,tmp[M],dis[M],L,R,mid;
    bool vis[M];
    
    inline void add(int x,int y,double z)
    {
       e[++ecnt].to = y;
       e[ecnt].d = z;
       e[ecnt].next = head[x];
       head[x] = ecnt;
    }
    
    inline void add1(int x,int y)
    {
       e1[++ecnt1].to = y;
       e1[ecnt1].next = head1[x];
       head1[x] = ecnt1;
    }
    
    void getG(int x,int fa)
    {
       size[x] = 1,hson[x] = 0;
       for(int i = head[x];i;i = e[i].next)
       {
          if(vis[e[i].to] || e[i].to == fa) continue;
          getG(e[i].to,x);
          size[x] += size[e[i].to],hson[x] = max(hson[x],size[e[i].to]);
       }
       hson[x] = max(hson[x],sum - size[x]);
       if(hson[x] < hson[G]) G = x;
    }
    
    void build(int x)
    {
       vis[x] = 1;
       for(int i = head[x];i;i = e[i].next)
       {
          if(vis[e[i].to] || size[e[i].to] < l) continue;
          sum = size[e[i].to],G = 0;
          getG(e[i].to,x),add1(x,G);
          build(G);
       }
       vis[x] = 0;
    }
    
    void getdis(int x,int fa,double d,int depth)
    {
       dis[depth] = max(dis[depth],d),mdep = max(mdep,depth);
       for(int i = head[x];i;i = e[i].next)
       {
          if(vis[e[i].to] || e[i].to == fa) continue;
          getdis(e[i].to,x,d+e[i].v-mid,depth+1);
       }
    }
    
    void solve(int x)
    {
       int cur = 0;
       vis[x] = 1,maxdep = 0;
       for(int i = head[x];i;i = e[i].next)
       {
          if(vis[e[i].to]) continue;
          cur++,mdep = 0;
          getdis(e[i].to,x,e[i].v-mid,1);
          maxdep = max(maxdep,mdep);
          if(cur != 1)
          {
    	 Head = 1,tail = 0;
    	 per(j,min(maxdep,u),l+1)
    	 {
    	    while(Head <= tail && tmp[q[tail]] <= tmp[j]) tail--;
    	    q[++tail] = j;
    	 }
    	 rep(j,0,mdep)
    	 {
    	    while(Head <= tail && tmp[q[tail]] <= tmp[l-j]) tail--;
    	    q[++tail] = l-j;
    	    while(Head <= tail && q[Head] + j > u) Head++;
    	    ans = max(ans,dis[j] + tmp[q[Head]]);
    	 }
          }
          rep(j,1,mdep) tmp[j] = max(tmp[j],dis[j]),dis[j] = -INF; 
       }
       rep(i,1,maxdep) tmp[i] = dis[i] = -INF;
       for(int i = head1[x];i;i = e1[i].next) solve(e1[i].to);
       vis[x] = 0;
    }
    
    int main()
    {
       n = read(),l = read(),u = read();
       rep(i,1,n-1) x = read(),y = read(),scanf("%lf",&z),add(x,y,z),add(y,x,z);
       L = 0,R = 1e6;
       G = 0,sum = n,hson[G] = n;
       getG(1,0),st = G,build(G);
       rep(i,1,n) dis[i] = tmp[i] = -INF;
       rep(t,1,35)
       {
          double mid = (L+R) / 2.0;
          ans = -INF,solve(st);
          if(ans > 0) L = mid;
          else R = mid;
       }
       printf("%.3lf
    ",L);
       return 0;
    }
    
    
  • 相关阅读:
    mysql之创建数据库,创建数据表
    mysql之group by,order by
    一个人选出2门以上不及格的课程sql语句
    GIt入门
    数据库索引工作原理
    题目:50个人围城一圈数到3和3的倍数时出圈,问剩下的人是谁?原来的位置是多少?
    约瑟夫环:递归算法
    K-means算法的java实现,聚类分析681个三国武将
    java用一个for循环输出99乘法表
    写一个基于UDP协议的聊天小程序
  • 原文地址:https://www.cnblogs.com/captain1/p/10092478.html
Copyright © 2011-2022 走看看