【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個也能夠翻倍的版本
相關文章
相關標籤/搜索