【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"
).
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.
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.)
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).
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) .
Then the next line contains nn integers represent the damage of the skills (every skill's damage is smaller than 1e81e8).
Output
One line with one integer.
思路
题意理解上有些歧义,从“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次也可以结算的话就没有必要对矩阵乘法分两种情况讨论了。
最大伤害可能有两种情况,即使用血棘(là)或不使用血辣。使用血辣的情况相当于在一个有向图上选择一条经过点数恰好为T的路径, 将路径上的点权和翻倍,然后在这条路径的首尾加上总数不超过(t-T)的点,使得总点权和最大。不使用血辣的情况则比较简单,直接选择一条经过点数不超过t的路径使得点权和最大即可。
联想到通过邻接矩阵乘法计算有向图上从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$合并起来即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = ;
int N, M, t, T, v[maxn];
struct Mat
{
LL A[maxn][maxn];
void Print() const
{
for(int i = ;i < N;++i)
for(int j = ;j < N;++j) printf("%lld%c", A[i][j], " \n"[j+ == N]);
puts("");
}
}; bool tp;//因为懒得把运算符重载改成函数所以用了一个全局变量,一般认为这样写是不严谨的
//tp=1时表示路径长度可以小于t
Mat operator + (const Mat &a, const Mat &b)
{
Mat ans;
for(int i = ;i < N;++i) for(int j = ;j < N;++j)
ans.A[i][j] = max(a.A[i][j], b.A[i][j]);
return ans;
} Mat operator * (const Mat &a, const Mat &b)
{
Mat ans;
for(int i = ;i < N;++i) for(int j = ;j < N;++j)
{
if(tp) ans.A[i][j] = max(a.A[i][j], b.A[i][j]);
else ans.A[i][j] = ;
for(int k = ;k < N;++k)
{
if(a.A[i][k] && b.A[k][j])
ans.A[i][j] = max(ans.A[i][j], a.A[i][k] + b.A[k][j] - v[k]);
}
}
return ans;
} Mat G, I;
void init()
{
scanf("%d%d%d%d", &N, &M, &t, &T);
int x, y;
while(M--)
{
scanf("%d%d", &x, &y);
--x, --y;
G.A[x][y] = ;
}
for(int i = ;i < N;++i) scanf("%d", &v[i]);
for(int i = ;i < N;++i) for(int j = ;j < N;++j)
{
if(G.A[i][j] == ) G.A[i][j] = v[i] + v[j];
}
for(int i = ;i < N;++i) for(int j = ;j < N;++j)
I.A[i][j] = ;
for(int i = ;i < N;++i)
I.A[i][i] = v[i];
} Mat powmod(Mat a, int n)
{
Mat ans = I;
while(n)
{
if(n & ) ans = ans * a;
a = a * a;
n >>= ;
}
return ans;
} Mat powmod2(Mat a, Mat g, int n)
{
Mat ans = a, pw = I;
a = a * g + g * a;
while(n)
{
if(n & )
{
ans = ans + pw * a + a * pw;
pw = pw * g + g * pw;
}
n >>= ;
a = a * g + g * a;
g = g * g;
}
return ans;
} void work()
{
tp = false;//必须够T次
Mat a = powmod(G, T-);
bool useXL = false;
for(int i = ;i < N;++i) for(int j = ;j < N;++j)
if(a.A[i][j])
{
useXL = true;
a.A[i][j] <<= ;
}
LL ans = ;
tp = true;//可以不足t次
if(useXL)
{
a = powmod2(a, G, t - T);
for(int i = ;i < N;++i) for(int j = ;j < N;++j) ans = max(ans, a.A[i][j]);
} a = powmod(G, t);
for(int i = ;i < N;++i) for(int j = ;j < N;++j) ans = max(ans, a.A[i][j]);
printf("%lld\n", ans);
} int main()
{
init();
work();
return ;
}
矩阵运算推广
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = ;
int N, M, t, T, v[maxn];
struct Mat
{
LL A[maxn][maxn];
void Print() const
{
for(int i = ;i < N;++i)
for(int j = ;j < N;++j) printf("%lld%c", A[i][j], " \n"[j+ == N]);
puts("");
}
}; Mat operator + (const Mat &a, const Mat &b)
{
Mat ans;
for(int i = ;i < N;++i) for(int j = ;j < N;++j)
ans.A[i][j] = max(a.A[i][j], b.A[i][j]);
return ans;
} Mat operator * (const Mat &a, const Mat &b)
{
Mat ans;
for(int i = ;i < N;++i) for(int j = ;j < N;++j)
{
ans.A[i][j] = max(a.A[i][j], b.A[i][j]);
for(int k = ;k < N;++k)
{
if(a.A[i][k] && b.A[k][j])
ans.A[i][j] = max(ans.A[i][j], a.A[i][k] + b.A[k][j] - v[k]);
}
}
return ans;
} Mat G, I;
void init()
{
scanf("%d%d%d%d", &N, &M, &t, &T);
int x, y;
while(M--)
{
scanf("%d%d", &x, &y);
--x, --y;
G.A[x][y] = ;
}
for(int i = ;i < N;++i) scanf("%d", &v[i]);
for(int i = ;i < N;++i) for(int j = ;j < N;++j)
{
if(G.A[i][j] == ) G.A[i][j] = v[i] + v[j];
}
for(int i = ;i < N;++i) for(int j = ;j < N;++j)
I.A[i][j] = ;
for(int i = ;i < N;++i)
I.A[i][i] = v[i];
} Mat powmod(Mat a, int n)
{
Mat ans = I;
while(n)
{
if(n & ) ans = ans * a;
a = a * a;
n >>= ;
}
return ans;
} Mat powmod2(Mat a, Mat g, int n)
{
Mat ans = a, pw = I;
a = a * g + g * a;
while(n)
{
if(n & )
{
ans = ans + pw * a + a * pw;
pw = pw * g + g * pw;
}
n >>= ;
a = a * g + g * a;
g = g * g;
}
return ans;
} void work()
{
Mat a = powmod(G, T-);
bool useXL = false;
for(int i = ;i < N;++i) for(int j = ;j < N;++j)
if(a.A[i][j])
{
useXL = true;
a.A[i][j] <<= ;
}
LL ans = ;
if(useXL)
{
a = powmod2(a, G, t - T);
for(int i = ;i < N;++i) for(int j = ;j < N;++j) ans = max(ans, a.A[i][j]);
} a = powmod(G, t);
for(int i = ;i < N;++i) for(int j = ;j < N;++j) ans = max(ans, a.A[i][j]);
printf("%lld\n", ans);
} int main()
{
init();
work();
return ;
}
不满T个也可以翻倍的版本
【ACM-ICPC 2018 徐州赛区网络预赛】E. End Fantasy VIX 血辣 (矩阵运算的推广)的更多相关文章
- ACM-ICPC 2018 徐州赛区网络预赛 G. Trace (思维,贪心)
ACM-ICPC 2018 徐州赛区网络预赛 G. Trace (思维,贪心) Trace 问答问题反馈 只看题面 35.78% 1000ms 262144K There's a beach in t ...
- ACM-ICPC 2018 徐州赛区网络预赛 J. Maze Designer (最大生成树+LCA求节点距离)
ACM-ICPC 2018 徐州赛区网络预赛 J. Maze Designer J. Maze Designer After the long vacation, the maze designer ...
- 计蒜客 1460.Ryuji doesn't want to study-树状数组 or 线段树 (ACM-ICPC 2018 徐州赛区网络预赛 H)
H.Ryuji doesn't want to study 27.34% 1000ms 262144K Ryuji is not a good student, and he doesn't wa ...
- ACM-ICPC 2018 徐州赛区网络预赛 B(dp || 博弈(未完成)
传送门 题面: In a world where ordinary people cannot reach, a boy named "Koutarou" and a girl n ...
- ACM-ICPC 2018 徐州赛区网络预赛 B. BE, GE or NE
In a world where ordinary people cannot reach, a boy named "Koutarou" and a girl named &qu ...
- ACM-ICPC 2018 徐州赛区网络预赛 H. Ryuji doesn't want to study
262144K Ryuji is not a good student, and he doesn't want to study. But there are n books he should ...
- ACM-ICPC 2018 徐州赛区网络预赛 F. Features Track
262144K Morgana is learning computer vision, and he likes cats, too. One day he wants to find the ...
- ACM-ICPC 2018 徐州赛区网络预赛 I. Characters with Hash
Mur loves hash algorithm, and he sometimes encrypt another one's name, and call him with that encryp ...
- ACM-ICPC 2018 徐州赛区网络预赛 D 杜教筛 前缀和
链接 https://nanti.jisuanke.com/t/31456 参考题解 https://blog.csdn.net/ftx456789/article/details/82590044 ...
- ACM-ICPC 2018 徐州赛区网络预赛(8/11)
ACM-ICPC 2018 徐州赛区网络预赛 A.Hard to prepare 枚举第一个选的,接下来的那个不能取前一个的取反 \(DP[i][0]\)表示选和第一个相同的 \(DP[i][1]\) ...
随机推荐
- MySQL -- JDBC
一 . JDBC的开发步骤 1.准备四大参数 2.注册驱动 3.获得连接 4.获得语句执行者 5.执行sql语句 6.处理结果 7.释放资源 1.准备四大参数 /* * jdbc四大配置参数 * &g ...
- 08 Packages 包
Packages Standard library Other packages Sub-repositories Community Standard library Name Synopsis ...
- 忘记SVN密码怎么办
1:下载TSvnPwd.exe 2:使用wireshark抓包.例如: PROPFIND /svn/dev2/!svn/vcc/default HTTP/1.1Host: 192.168.156.1: ...
- JS Ajax异步请求发送列表数据后面多了[]
还在苦逼的写代码,这里就不详细了,直接抛出问题: 如图所示: 前端ajax请求向后端发送数据的时候,给key添加了[]出现很多找不到原因, 后面在说 解决方法: 暂时先这样记录一下,下次方便查找,好了 ...
- Python获取指定文件夹下的文件名
本文采用os.walk()和os.listdir()两种方法,获取指定文件夹下的文件名. 一.os.walk() 模块os中的walk()函数可以遍历文件夹下所有的文件. os.walk(top, t ...
- JDK安装及配置 (tar.gz版)和tomcat的安装
jdk下载: 我们这里下载了jdk-8u65-linux-x64.tar.gz. 官网:http://www.oracle.com/technetwork/java/javase/downloads/ ...
- 【58沈剑架构系列】互联网公司为啥不使用mysql分区表?
缘起:有个朋友问我分区表在58的应用,我回答不出来,在我印象中,百度.58都没有听说有分区表相关的应用,业内进行一些技术交流的时候也更多的是自己分库分表,而不是使用分区表.于是去网上查了一下,并询问了 ...
- jQuery类名添加click方法
通过$("").jQuery为元素添加点击事件,在使用类的情况下,可以使用$(e.target).attr('title');获得被点击元素的属性值. 示例代码如下 $(" ...
- php 根据ip获取城市以及网络运营商名称(利用qqwry.dat)
根据用户IP地址判定出所在城市以及网络运营商 qqwry.dat下载地址:http://files.cnblogs.com/guangxiaoluo/qqwry.rar 解压出来即可 //获取用户真 ...
- 【51nod】1565 模糊搜索
题解 这个字符集很小,我们可以把每个字符拿出来做一次匹配,把第一个字符串处理每个出现过的该字符处理成一个区间加,即最后变成第一个字符串的该位置能够匹配某字符 例如对于样例 10 4 1 AGCAATT ...