BZOJ 1063 NOI 2008 设计路线 树形DP

11 篇文章 0 订阅
8 篇文章 0 订阅

求树的路径覆盖方案数使各点到根经过非覆盖边总次数(不便利值)最小。
OI题题面真是麻烦。。
f[i][j][k] 表示第i个点,向子树连了k条边后的不便利值。显然 0k2
如果令 gi 表示子树向本点连一条边,不便利值相同的方案数, hi 表示子树向本点不连边,不便利值+1的方案数。
那么
gi=f[i][j][0]+f[i][j][1]
hi=f[i][j1][0]+f[i][j1][1]+f[i][j1][2]
f[i][j][0]=padjihp
f[i][j][1]=padji(gpqadjiq>phq)
f[i][j][2]=padjiqadjiq>p(gpgqradjir>qhr)
注意重复统计的问题,保证无序性。
然后因为每次只需要统计前半段,因此可以改写一下式子,令 q 表示p的上一个兄弟。

  • fp[i][j][0]=fq[i][j][0]hp,即之前取0个的方案数*本次取0的方案数。

    • fp[i][j][1]=fq[i][j][1]hp+fq[i][j][0]gp ,即之前取1个的方案数 * 本次不取的方案数+之前不取的方案数 * 本次取1个的方案数。
    • fp[i][j][2]=fq[i][j][2]hp+fq[i][j][1]gp ,即之前取2个的方案数 * 本次不取的方案数+之前取1个的方案数 * 本次取1个的方案数。
    • 然后最小的不便利值是 O(logV) 的(比如轻链是 O(logV) 的),所以总复杂度 O(ElogV)

      #include <cstdio>
      #include <cstring>
      #define FOR(i,j,k) for(int i=j;i<=k;++i)
      #define adj(i,x) for(int i=h[x];i;i=p[i])
      const int N = 100001, M = 200001;
      typedef long long ll;
      
      int read() {
          int s = 0, f = 1; char ch = getchar();
          while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
          while('0'<=ch&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
          return s * f;
      }
      
      int h[N], p[M], v[M], vis[N], cnt = 0, q, uncon;
      ll f[N][11][3];
      void add(int a, int b) { p[++cnt] = h[a]; v[cnt] = b; h[a] = cnt; }
      ll get(ll x) { return !x ? 0 : x % q ? x % q : q; }
      void dfs(int u) { vis[u] = 1; adj(i,u) if(!vis[v[i]]) dfs(v[i]); }
      
      void dp(int u, int fa) {
          ll &a = f[u][uncon][0], &b = f[u][uncon][1], &c = f[u][uncon][2], f1, f2;
          a = 1; b = 0; c = 0;
          adj(i, u) {
              if (v[i] == fa) continue;
              dp(v[i], u);
              f1 = f[v[i]][uncon][0] + f[v[i]][uncon][1];
              if (!uncon) f2 = 0;
              else f2 = f[v[i]][uncon - 1][0] + f[v[i]][uncon - 1][1] + f[v[i]][uncon - 1][2];
              c = get(c * f2 + b * f1);
              b = get(b * f2 + a * f1);
              a = get(a * f2);
          }
      }
      
      int main() {
          int n = read(), m = read(), x, y;
          q = read();
          FOR(i,1,m) x = read(), y = read(), add(x, y), add(y, x);
          dfs(1);
          FOR(i,1,n) if(!vis[i]) return printf("-1\n-1\n"), 0;
          for (uncon = 0; ; uncon++) {
              dp(1, -1);
              if (f[1][uncon][0] + f[1][uncon][1] + f[1][uncon][2])
                  return printf("%d\n%lld", uncon, (f[1][uncon][0] + f[1][uncon][1] + f[1][uncon][2]) % q), 0;
          }
      }

      [NOI2008] 设计路线

      ★★★★ 输入文件:design.in 输出文件:design.out 简单对比
      时间限制:2 s 内存限制:128 MB

      【问题描述】

      Z 国坐落于遥远而又神奇的东方半岛上,在小Z 的统治时代公路成为这里主要的交通手段。Z 国共有n 座城市,一些城市之间由双向的公路所连接。非常神奇的是Z 国的每个城市所处的经度都不相同,并且最多只和一个位于它东边的城市直接通过公路相连。Z 国的首都是Z 国政治经济文化旅游的中心,每天都有成千上万的人从Z 国的其他城市涌向首都。
      为了使Z 国的交通更加便利顺畅,小Z 决定在Z 国的公路系统中确定若干条规划路线,将其中的公路全部改建为铁路。
      我们定义每条规划路线为一个长度大于1 的城市序列,每个城市在该序列中最多出现一次,序列中相邻的城市之间由公路直接相连(待改建为铁路)。并且,每个城市最多只能出现在一条规划路线中,也就是说,任意两条规划路线不能有公共部分。
      当然在一般情况下是不可能将所有的公路修建为铁路的,因此从有些城市出发去往首都依然需要通过乘坐长途汽车,而长途汽车只往返于公路连接的相邻的城市之间,因此从某个城市出发可能需要不断地换乘长途汽车和火车才能到达首都。
      我们定义一个城市的“不便利值”为从它出发到首都需要乘坐的长途汽车的次数,而Z 国的交通系统的“不便利值”为所有城市的不便利值的最大值,很明显首都的“不便利值”为0。小Z 想知道如何确定规划路线修建铁路使得Z 国的交通系统的“不便利值”最小,以及有多少种不同的规划路线的选择方案使得“不便利值”达到最小。当然方案总数可能非常大,小Z 只关心这个天文数字mod Q 后的值。
      注意:规划路线1-2-3 和规划路线3-2-1 是等价的,即将一条规划路线翻转依然认为是等价的。两个方案不同当且仅当其中一个方案中存在一条规划路线不属于另一个方案。

      【输入格式】

      输入文件第一行包含三个正整数N、M、Q,其中N 表示城市个数,M 表示公路总数,N 个城市从1~N 编号,其中编号为1 的是首都。Q 表示上文提到的设计路线的方法总数的模数。接下来M 行,每行两个不同的正数ai、bi (1≤ ai , bi ≤ N)表示有一条公路连接城市ai 和城市bi。 输入数据保证一条公路只出现一次。

      【输出格式】

      输出文件应包含两行。第一行为一个整数,表示最小的“不便利值”。 第二行为一个整数,表示使“不便利值”达到最小时不同的设计路线的方法总数 mod Q 的值。
      如果某个城市无法到达首都,则输出两行-1。

      【输入样例】

      5 4 100
      1 2
      4 5
      1 3
      4 1

      【输出样例】

      1
      10

      【样例说明】

      以下样例中是10 种设计路线的方法:
      (1) 4-5
      (2) 1-4-5
      (3) 4-5, 1-2
      (4) 4-5, 1-3
      (5) 4-5, 2-1-3
      (6) 2-1-4-5
      (7) 3-1-4-5
      (8) 1-4
      (9) 2-1-4
      (10) 3-1-4

      【数据规模和约定】

      对于20%的数据,满足N,M ≤ 10。
      对于50%的数据,满足N,M ≤ 200。
      对于60%的数据,满足N,M ≤ 5000。
      对于100%的数据,满足1 ≤ N,M ≤ 100000,1 ≤ Q ≤ 120000000。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值