【ACM-ICPC 2018 徐州赛区网络预赛】E. End Fantasy VIX 血辣 (矩阵运算的推广)

Morgana is playing a game called End Fantasy VIX. In this game, characters have nn skills, every skill has its damage. And using skill has special condition. Briefly speaking, if this time you use skill "x", then next time you can use skill "y" (just like combo). There are mm conditions (xi, yiy_i), and you can't break the rules. (that means, if you don't have any condition that equals to (xx, yy), then you can't use "y" after use "x").c++

Now, Morgana wants to defeat the boss, he can use skills t times. In the first time he can use any skill, then he should obey the rules. Besides, he has a special armor called "Xue La", he can use this armor and add a debuff to the boss. The debuff will record damage and when it is over, the record damage will be caused again. (that means double damage) The debuff will continue TT times, and he can use this armor in any time, it won't be in conflict with skills.ide

Finally, Morgana wants to maximize the damage, but it is too difficult. So please help him deal with this problem.函数

(If Morgana can not use any skill at a time, he will finish the game and the final damage is his total damage at this time.)this

Input

First line contains 44 integers n,m,t,T (2≤n≤642 \le n \le 64, 1≤m≤n×(n−1)1 \le m \le n \times (n-1) , 1≤t≤1e9, 1≤T≤t).spa

In the next mm lines each line contains two integers represent condition (xi,yix_i, y_i) (xi,yi≤nx_i, y_i \le n) .code

Then the next line contains nn integers represent the damage of the skills (every skill's damage is smaller than 1e81e8).blog

Output

One line with one integer.ip

 

思路

题意理解上有些歧义,从“If Morgana can not use any skill at a time, he will finish the game and the final damage is his total damage at this time.”这句理解的话应该是必须满T次才能够翻倍的,但听说场上有clarification说不满T次也能够结算。这篇题解是按照“必须满T次”写的,若是要改为不满T次也能够结算的话就没有必要对矩阵乘法分两种状况讨论了。ci

最大伤害可能有两种状况,即便用血棘()或不使用血辣。使用血辣的状况至关于在一个有向图上选择一条通过点数刚好为T的路径, 将路径上的点权和翻倍,而后在这条路径的首尾加上总数不超过(t-T)的点,使得总点权和最大。不使用血辣的状况则比较简单,直接选择一条通过点数不超过t的路径使得点权和最大便可。it

 

联想到经过邻接矩阵乘法计算有向图上从u到v长度为T的路径条数的思路,不妨尝试将这里的问题转化成能够在矩阵上计算的问题。

 

用矩阵$A_{ij}$来表示图上从i到j的某些路径的点权和的最大值,若是路径不存在则定义为0。


用“路径合并”运算(即当一条路径的终点与另外一条路径的起点均为k时,定义合并的结果为两条路径合并后的点权和)和$\max$运算重定义矩阵乘法:$(A \cdot B)_{ij} = \max_{k}\{A_{ik}\ \mathop{Merge}\ B_{kj}\}$.

 

因为$\max$运算可交换、可结合、存在单位元0(因为题目中点权均为正数),路径合并运算可结合,且$\max$对“路径合并”知足分配律(即$\max(A_{ik}, B_{ik}) \ \mathop{Merge}\ C_{kj} = \max(A_{ik}\ \mathop{Merge}\ C_{kj},B_{ik}\ \mathop{Merge}\ C_{kj})$),可知重定义后的矩阵乘法是可结合的,便可以用快速幂的思路进行分治计算。

 

 

考虑上述运算的实际意义,若是咱们将题目给出的有向图写成具备上述性质的矩阵$G$,则$G^{T-1}_{ij}$即为从i到j刚好通过T-1条边(即T个点)的全部路径的最大点权和。这样咱们就知道,想要用血辣的话,只要对$G^{T-1}$中的每一个元素翻倍就能够了。

 

最后的问题就是如何在这段使用血辣的路径先后加上总数不超过$(t-T)$的点使得路径长度最大。仍然是用快速幂的思路,咱们只要考虑在(t-T)个(G+I)的连乘之间任选一个位置插入刚才翻倍后的$G^{T-1}$,把全部状况用$\max$合并起来便可。

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 typedef long long LL;
  4 const int maxn = 64;
  5 int N, M, t, T, v[maxn];
  6 struct Mat
  7 {
  8     LL A[maxn][maxn];
  9     void Print() const
 10     {
 11         for(int i = 0;i < N;++i)
 12             for(int j = 0;j < N;++j) printf("%lld%c", A[i][j], " \n"[j+1 == N]);
 13         puts("");
 14     }
 15 };
 16 
 17 bool tp;//由于懒得把运算符重载改为函数因此用了一个全局变量,通常认为这样写是不严谨的
 18         //tp=1时表示路径长度能够小于t
 19 Mat operator + (const Mat &a, const Mat &b)
 20 {
 21     Mat ans;
 22     for(int i = 0;i < N;++i) for(int j = 0;j < N;++j)
 23         ans.A[i][j] = max(a.A[i][j], b.A[i][j]);
 24     return ans;
 25 }
 26 
 27 Mat operator * (const Mat &a, const Mat &b)
 28 {
 29     Mat ans;
 30     for(int i = 0;i < N;++i) for(int j = 0;j < N;++j)
 31     {
 32         if(tp) ans.A[i][j] = max(a.A[i][j], b.A[i][j]);
 33         else ans.A[i][j] = 0;
 34         for(int k = 0;k < N;++k)
 35         {
 36             if(a.A[i][k] && b.A[k][j])
 37                 ans.A[i][j] = max(ans.A[i][j], a.A[i][k] + b.A[k][j] - v[k]);
 38         }
 39     }
 40     return ans;
 41 }
 42 
 43 Mat G, I;
 44 void init()
 45 {
 46     scanf("%d%d%d%d", &N, &M, &t, &T);
 47     int x, y;
 48     while(M--)
 49     {
 50         scanf("%d%d", &x, &y);
 51         --x, --y;
 52         G.A[x][y] = 1;
 53     }
 54     for(int i = 0;i < N;++i) scanf("%d", &v[i]);
 55     for(int i = 0;i < N;++i) for(int j = 0;j < N;++j)
 56     {
 57         if(G.A[i][j] == 1) G.A[i][j] = v[i] + v[j];
 58     }
 59     for(int i = 0;i < N;++i) for(int j = 0;j < N;++j)
 60         I.A[i][j] = 0;
 61     for(int i = 0;i < N;++i)
 62         I.A[i][i] = v[i];
 63 }
 64 
 65 Mat powmod(Mat a, int n)
 66 {
 67     Mat ans = I;
 68     while(n)
 69     {
 70         if(n & 1) ans = ans * a;
 71         a = a * a;
 72         n >>= 1;
 73     }
 74     return ans;
 75 }
 76 
 77 Mat powmod2(Mat a, Mat g, int n)
 78 {
 79     Mat ans = a, pw = I;
 80     a = a * g + g * a;
 81     while(n)
 82     {
 83         if(n & 1)
 84         {
 85             ans = ans + pw * a + a * pw;
 86             pw = pw * g + g * pw;
 87         }
 88         n >>= 1;
 89         a = a * g + g * a;
 90         g = g * g;
 91     }
 92     return ans;
 93 }
 94 
 95 void work()
 96 {
 97     tp = false;//必须够T次
 98     Mat a = powmod(G, T-1);
 99     bool useXL = false;
100     for(int i = 0;i < N;++i) for(int j = 0;j < N;++j)
101         if(a.A[i][j])
102         {
103             useXL = true;
104             a.A[i][j] <<= 1;
105         }
106     LL ans = 0;
107     tp = true;//能够不足t次
108     if(useXL)
109     {
110         a = powmod2(a, G, t - T);
111         for(int i = 0;i < N;++i) for(int j = 0;j < N;++j) ans = max(ans, a.A[i][j]);
112     }
113 
114     a = powmod(G, t);
115     for(int i = 0;i < N;++i) for(int j = 0;j < N;++j) ans = max(ans, a.A[i][j]);
116     printf("%lld\n", ans);
117 }
118 
119 int main()
120 {
121     init();
122     work();
123     return 0;
124 }
矩阵运算推广
 
  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 typedef long long LL;
  4 const int maxn = 64;
  5 int N, M, t, T, v[maxn];
  6 struct Mat
  7 {
  8     LL A[maxn][maxn];
  9     void Print() const
 10     {
 11         for(int i = 0;i < N;++i)
 12             for(int j = 0;j < N;++j) printf("%lld%c", A[i][j], " \n"[j+1 == N]);
 13         puts("");
 14     }
 15 };
 16 
 17 Mat operator + (const Mat &a, const Mat &b)
 18 {
 19     Mat ans;
 20     for(int i = 0;i < N;++i) for(int j = 0;j < N;++j)
 21         ans.A[i][j] = max(a.A[i][j], b.A[i][j]);
 22     return ans;
 23 }
 24 
 25 Mat operator * (const Mat &a, const Mat &b)
 26 {
 27     Mat ans;
 28     for(int i = 0;i < N;++i) for(int j = 0;j < N;++j)
 29     {
 30         ans.A[i][j] = max(a.A[i][j], b.A[i][j]);
 31         for(int k = 0;k < N;++k)
 32         {
 33             if(a.A[i][k] && b.A[k][j])
 34                 ans.A[i][j] = max(ans.A[i][j], a.A[i][k] + b.A[k][j] - v[k]);
 35         }
 36     }
 37     return ans;
 38 }
 39 
 40 Mat G, I;
 41 void init()
 42 {
 43     scanf("%d%d%d%d", &N, &M, &t, &T);
 44     int x, y;
 45     while(M--)
 46     {
 47         scanf("%d%d", &x, &y);
 48         --x, --y;
 49         G.A[x][y] = 1;
 50     }
 51     for(int i = 0;i < N;++i) scanf("%d", &v[i]);
 52     for(int i = 0;i < N;++i) for(int j = 0;j < N;++j)
 53     {
 54         if(G.A[i][j] == 1) G.A[i][j] = v[i] + v[j];
 55     }
 56     for(int i = 0;i < N;++i) for(int j = 0;j < N;++j)
 57         I.A[i][j] = 0;
 58     for(int i = 0;i < N;++i)
 59         I.A[i][i] = v[i];
 60 }
 61 
 62 Mat powmod(Mat a, int n)
 63 {
 64     Mat ans = I;
 65     while(n)
 66     {
 67         if(n & 1) ans = ans * a;
 68         a = a * a;
 69         n >>= 1;
 70     }
 71     return ans;
 72 }
 73 
 74 Mat powmod2(Mat a, Mat g, int n)
 75 {
 76     Mat ans = a, pw = I;
 77     a = a * g + g * a;
 78     while(n)
 79     {
 80         if(n & 1)
 81         {
 82             ans = ans + pw * a + a * pw;
 83             pw = pw * g + g * pw;
 84         }
 85         n >>= 1;
 86         a = a * g + g * a;
 87         g = g * g;
 88     }
 89     return ans;
 90 }
 91 
 92 void work()
 93 {
 94     Mat a = powmod(G, T-1);
 95     bool useXL = false;
 96     for(int i = 0;i < N;++i) for(int j = 0;j < N;++j)
 97         if(a.A[i][j])
 98         {
 99             useXL = true;
100             a.A[i][j] <<= 1;
101         }
102     LL ans = 0;
103     if(useXL)
104     {
105         a = powmod2(a, G, t - T);
106         for(int i = 0;i < N;++i) for(int j = 0;j < N;++j) ans = max(ans, a.A[i][j]);
107     }
108 
109     a = powmod(G, t);
110     for(int i = 0;i < N;++i) for(int j = 0;j < N;++j) ans = max(ans, a.A[i][j]);
111     printf("%lld\n", ans);
112 }
113 
114 int main()
115 {
116     init();
117     work();
118     return 0;
119 }
不满T个也能够翻倍的版本
相关文章
相关标签/搜索