2019-03-29日机房爆零日记
T1:机器人
【题目描述】早苗入手了最新的 Gundam 模型。最新款自然有着与以往不同的功能,是它能够自动行走,厉害吧。早苗的新模型可以按照输入的命令进行移动,命令包括‘E’、‘S’、‘W’四种,分别对应东南西北。执行某个命令时,它会向对应方向移动一个单位为新型机器人,它可以执行命令串。对于输入的命令串,每一秒它会按命令一次。执行完命令串的最后一个命令后,会自动从头开始循环。在 0 时刻时人位于(0,0)。求 T 秒后机器人所在位置坐标。
【输入格式】第 1 行:一个字符串,表示早苗输入的命令串,保证至少有 1 个命令
第 2 行:一个正整数 T
【输出格式】2 个整数,表示 T 秒时,机器人的坐标。
【样例输入】
NSWWNSNEEWN
12
【样例输出】
-1 3
【数据范围】对于 60%的数据 T<=500,000 且命令串长度<=5,000
对于 100%的数据 T<=2,000,000,000 且命令串长度<=5,0
(这道题我是不是在某谷上做过了???)
首先一看题目,非常简单明了、清晰明白,一道赤裸裸的模拟题,毫无困难的算法思想对吧。然后再一看数据范围,2后面九个零,二十亿个命令。显然 O ( n ) O(n) O(n)算法行不通。所以我们应该怎么办?
简单一想发现这是在进行重复劳动。所以我们只要把总命令数用命令条的字符数mod一下就好了。那么我们这个时候一共会进行 T / l e n g t h ( s ) T/length(s) T/length(s)次命令串操作。显然的,我们只需要计算一次命令串的操作,再将这个操作放大,最后加上命令串的前 T m o d    l e n g t h ( s ) T \mod length(s) Tmodlength(s)次操作,就可以得到最终位置。
所以,爆零赛现场并未在此题逗留,然后华丽丽的挂在了T2和T3……
T2:数列
【题目描述】
a[1]=a[2]=a[3]=1;a[x]=a[x-3]+a[x-1] (x>3)
求 a 数列的第 n 项对 1000000007(10^9+7)取余的值。
【输入格式】
第一行一个整数 T,表示询问个数。
以下 T 行,每行一个正整数 n。
【输出格式】
每行输出一个非负整数表示答案。
【样例输入】
3
6
8
10
【样例输出】
4
9
19
【数据范围】
对于 30%的数据 n<=100;
对于 60%的数据 n<=210^7;
对于 100%的数据 T<=100,n<=210^9;
这个题目嘛,看到的第一眼就发现这个数列像Fibonacci数列,然后边上已经被数学逼疯的童鞋直接开始推通项公式……然而并无卵用。
想不到正解,那就先骗分吧。
30%
三十分的代码异常简单,就直接手敲一个在线的暴力算法,一点问题都没有。
while (T--){
cin >> n;
f[1]=f[2]=f[3]=1;
for (int i=4;i<=n;i++)
f[i]=f[i-1]+f[i-3];
cout << f[n] << endl;
}
60%
六十分……也很简单,直接离线打表,读入n,输出f[n]
f[1]=f[2]=f[3]=1;
for (int i=4;i<=maxx;i++)
f[i]=f[i-1]+f[i-3];
while (T--){
cin >> n;
cout << f[n] << endl;
}
100%
这个算法还真的想不出来……毕竟以前没有接触过矩阵加速这个东西。是在赛后想水一发某谷,忽然就找到了这道题,竟然……一模一样!!!传送门
然后果断翻了题解。本人蒟蒻一枚,对这个算法不怎么熟悉,就在下面说说我的见解:
矩阵加速,首先一定要有矩阵。这个矩阵,是这样子的:
[
F
[
i
]
F
[
i
−
1
]
F
[
i
−
2
]
]
\begin{bmatrix} &F[i]& \\ & F[i-1]&\\ & F[i-2]& \end{bmatrix}
⎣⎡F[i]F[i−1]F[i−2]⎦⎤
然后显而易见的,我们可以将这个矩阵里的项从
f
[
i
−
1
]
,
f
[
i
−
2
]
,
f
[
i
−
3
]
f[i-1],f[i-2],f[i-3]
f[i−1],f[i−2],f[i−3]这么三项里推导出来,就像这样子:
∵
f
[
i
]
=
f
[
i
−
1
]
+
f
[
i
−
3
]
∴
f
[
i
]
=
f
[
i
−
1
]
×
1
+
f
[
i
−
2
]
×
0
+
f
[
i
−
3
]
×
1
f
[
i
−
1
]
=
f
[
i
−
1
]
×
1
+
f
[
i
−
2
]
×
0
+
f
[
i
−
3
]
×
0
f
[
i
−
2
]
=
f
[
i
−
1
]
×
0
+
f
[
i
−
2
]
×
1
+
f
[
i
−
3
]
×
0
\because f[i]=f[i-1]+f[i-3] \\ \therefore f[i]=f[i-1]\times 1 + f[i-2]\times 0 + f[i-3]\times 1\\ f[i-1]=f[i-1]\times 1+f[i-2]\times 0+f[i-3]\times 0\\ f[i-2]=f[i-1]\times 0+f[i-2] \times 1+f[i-3]\times 0
∵f[i]=f[i−1]+f[i−3]∴f[i]=f[i−1]×1+f[i−2]×0+f[i−3]×1f[i−1]=f[i−1]×1+f[i−2]×0+f[i−3]×0f[i−2]=f[i−1]×0+f[i−2]×1+f[i−3]×0
然后我们可以将三个递推式里的系数提取出来,构成一个矩阵,就像这样:
[
1
0
1
1
0
0
0
1
0
]
\begin{bmatrix} 1 & 0 &1 \\ 1 & 0 &0 \\ 0 &1 &0 \end{bmatrix}
⎣⎡110001100⎦⎤
然后我们发现,就可以使用矩阵快速幂来对这个初始矩阵进行
n
−
3
n-3
n−3次幂的求解就可以了。
关于矩阵快速幂,还真没什么好说的,仅仅就是把普通快速幂里的乘法换成了矩阵乘法而已。对于掌握快速幂和对矩阵有一点了解的人都可以非常轻松的掌握。
然后下面就是我们的代码了老师我相信你不会想看P的代码,就拷了一下标程(逃:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
const int mod=1000000007;
typedef long long arr[3][3];
int n,T;
long long ans;
arr opt={{0,0,1},{1,0,0},{0,1,1}},b,t;
using namespace std;
void mul(arr a,arr b,arr c){ //矩阵乘法
memset(t,0,sizeof(t));
for (int k=0;k<3;k++)
for (int i=0;i<3;i++)
if (a[i][k])
for (int j=0;j<3;j++)
t[i][j]+=a[i][k]*b[k][j];//其实这里就是把A*B赋值到T矩阵里
for (int i=0;i<3;i++)
for (int j=0;j<3;j++)
if (t[i][j]<mod) c[i][j]=t[i][j];
else c[i][j]=t[i][j] % mod; //但我不明白为什么一定要分开来模
//两个循环应该可以合并成一个的
}
int main(){
cin >> T;
while (T--){
cin >> n;
arr a={{1,0,0},{0,1,0},{0,0,1}};
memcpy(b,opt,sizeof(b));
for (n-=3;n>0;n>>=1){ //快速幂的板子
if (n&1) mul(a,b,a); //原来这里应该是a*=b;
mul(b,b,b);//原来这里应该是b*=b;
}
ans=(a[2][0]+a[2][1]+a[2][2]) % mod;//这里就是提取矩阵的第三行,加起来之后就是答案
printf("%d\n",ans);
}
return 0;
}
T3:Holes
Problem 3 虫洞(holes.cpp/c/pas)
【题目描述】
N 个虫洞,M 条单向跃迁路径。从一个虫洞沿跃迁路径到另一个虫洞需要消耗一定量的燃料和 1 单位时间。虫洞有白洞和黑洞之分。设一条跃迁路径两端的虫洞质量差为 delta。
1.从白洞跃迁到黑洞,消耗的燃料值减少 delta,若该条路径消耗的燃料值变为负数的话,取为 0。
2.从黑洞跃迁到白洞,消耗的燃料值增加 delta。
3.路径两端均为黑洞或白洞,消耗的燃料值不变化。
作为压轴题,自然不会是如此简单的最短路问题,所以每过 1 单位时间黑洞变为白洞,白洞变为黑洞。在飞行过程中,可以选择在一个虫洞停留 1 个单位时间,如果当前为白洞,则不消耗燃料,否则消耗 s[i]的燃料。现在请你求出从虫洞 1 到 N 最少的燃料消耗,保证一定存在 1 到 N 的路线。
【输入格式】
第 1 行:2 个正整数 N,M
第 2 行:N 个整数,第 i 个为 0 表示虫洞 i 开始时为白洞,1 表示黑洞。
第 3 行:N 个整数,第 i 个数表示虫洞 i 的质量 w[i]。
第 4 行:N 个整数,第 i 个数表示在虫洞 i 停留消耗的燃料 s[i]。
第 5…M+4 行:每行 3 个整数,u,v,k,表示在没有影响的情况下,从虫洞 u到虫洞 v 需要消耗燃料 k。
【输出格式】
一个整数,表示最少的燃料消耗。
【样例输入】
4 5
1 0 1 0
10 10 100 10
5 20 15 10
1 2 30
2 3 40
1 3 20
1 4 200
3 4 200
【样例输出】
130
【数据范围】
对于 30%的数据: 1<=N<=100,1<=M<=500
对于 60%的数据: 1<=N<=1000,1<=M<=5000
对于 100%的数据: 1<=N<=5000,1<=M<=30000
其中 20%的数据为 1<=N<=3000 的链 1<=u,v<=N, 1<=k,w[i],s[i]<=2
T3这道题从看到开始心态就是炸裂的,一开始干脆就直接手打了一个bfs爆搜,直接完美爆零……
直接赛后看题解,发现这题好像也不难……
因为黑洞与白洞的性质完全不一样,我们干脆可以把一个虫洞拆成一个黑洞和一个白洞,然后根据题目的意思不断连边,就可以构成一个n*2的有向无环图。然后再跑一遍SPFA,就可以妥妥的满分了……
我比赛的时候是脑抽吗,竟然去打了bfs……
#include<cstdio>
#include<cstring>
#define rep(i,n) for(int i=1;i<=n;i++)
#define abs(x) (x > 0 ? x : -(x))
const int maxn=10010,maxm=70010,maxq=16383;
int n,m,u,v,k,sz,l,r,x,delta,p[maxn],w[maxn],d[maxn],q[maxq+1],en[maxn],pre[maxm],g[maxm],len[maxm];
bool Inq[maxn];
inline void Ins(int u,int v,int d){
pre[++sz]=en[u];en[u]=sz;g[sz]=v;len[sz]=d;
}
void Init_And_Set_Map(){
scanf("%d%d",&n,&m);
rep(i,n) scanf("%d",&p[i]);
rep(i,n) scanf("%d",&w[i]);
rep(i,n){
scanf("%d",&k);
Ins(i+i-1,i+i,0);
Ins(i+i,i+i-1,k);
}
rep(i,m){
scanf("%d%d%d",&u,&v,&k);
if (p[u]==p[v]){
Ins(u+u-1,v+v,k);
Ins(u+u,v+v-1,k);
}else{
delta=abs(w[u]-w[v]);
Ins(u+u-1,v+v-1, (k-delta>0 ? k-delta : 0));
Ins(u+u,v+v, k+delta);
}
}
}
void SPFA()
{
memset(d,0x7F,sizeof(d));
q[0]=p[1]+1;d[q[0]]=l=r=0;
while ((r+1&maxq) != l){
x=q[l];l=(l+1)&maxq;Inq[x]=0;
for(int i=en[x];i;i=pre[i]){
v=g[i];
if (d[x]+len[i]<d[v]){
d[v]=d[x]+len[i];
if (Inq[v]) continue;
Inq[v]=1;
if (d[v]<d[q[l]]) q[l=(l-1)&maxq]=v;
else q[r=(r+1)&maxq]=v;
}
}
}
}
int main(){
Init_And_Set_Map();
SPFA();
printf("%d",d[n+n-1]<d[n+n] ? d[n+n-1] : d[n+n]);
return 0;
}