文章目录
前方高能!!!
总论
可以使用倍增的情况是这一种情况可以任意划分。
AcWing\293. 开车旅行
输入样例:
10
4 5 6 1 2 3 7 8 9 10
7
10
1 7
2 7
3 7
4 7
5 7
6 7
7 7
8 7
9 7
10 7
输出样例:
2
3 2
2 4
2 1
2 4
5 1
5 1
2 1
2 0
0 0
0 0
这一道题目真的是难中难!!!
注意题目中所说的“且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为 H i H_i Hi。”.注意这些城市并不是排列在同一条线上,可能是1号到3号的距离更近。但是总体上的趋势是往东走。
现在思考,对于每一个点,都会向东走,走到距离最小或者是最小的地方。
由于:
- 各个城市的海拔高度互不相同
- 当前城市到两个城市的距离相同,则认为离海拔低的那个城市更近
所以每一个点(无论是A开车还是B开车)到达的下一个点的位置是固定的。SO?如果确定了起始点,那么所走的路线是唯一固定的。
part1 确定ga[], gb[]
由于最大值与最小值是在目前所处的点的东方,所以遍历的时候从右向左。遍历完一个就加上值。
使用set来求邻值。
对set的使用方法进行分析:
邻值肯定在与当前值最近的地方。如果最近的是2,那么次近的就是1或者3. 反之,如果最近的是3,那么次近的是2或者4.
所以只需要关注离他最近的这是个值。
为了方便,可以在set中存入两个正无穷以及两个负无穷!
注意:由于set不能含有相同的元素,所以两个“正无穷”不能相等
注意:0x3f3f3f3f不一定可以代表正无穷!他的值是1,061,109,567,大概是 1 0 9 10^9 109,当可能存在的最大值大于 1 0 9 10^9 109时,应该选择其他最大值,甚至是选择long long
。
part2 确定f[]
对于f[]
的确定,代码不是太难,重点是要学会使用倍增求出。
f[i][j][k]
表示从j
出发,k
先开车,走过 2 i 2^i 2i所到达的位置。
k == 0表示小A开车
- 显然
f[0][j][0] = ga[j]
,f[0][j][1] = gb[j]
- 当
j==1
,有**(注意:与3不同的原因是 2 1 2^1 21的一半不再是偶数,初始开车的人不同)**
f[1][j][0] = f[0][ f[0][j][0] ][1]
(先小A开一下,然后小B在小A开到的位置继续开。
f[1][j][1] = f[0][ f[0][j][1] ][0]
- 当
j > 1
,有
f[i][j][0] = f[i-1][ f[i-1][j][0] ][0]
f[i][j][1] = f[i-1][ f[i-1][j][1] ][1]
f 的第一维的确定:由于使用倍增。而 2 16 = 65 , 536 2^{16} =65,536 216=65,536所以17已经够使用。
part3 确定da[]
和db[]
同样需要找到转移关系。
da[i][j][k]
表示从j
出发,k
先开车,走过 2 i 2^i 2i,小A所走过的所有距离。
db[i][j][k]
表示从j
出发,k
先开车,走过 2 i 2^i 2i,小B所走过的所有距离。
与上面相同,分为以下三种情况:
da[0][j][0] = dist(j, f[0][j][0]), db[0][j][0] = 0
db[0][j][1] = dist(j, f[0][j][1]), da[0][j][1] = 0
- 当
i == 1
有:
da[1][j][k] = da[0][j][k] + da[0][ f[0][j][k] ][1-k]
db[1][j][k] = db[0][j][k] + db[0][ f[0][j][k] ][1-k]
- 当
i > 1
,有:
da[i][j][k] = da[i-1][j][k] + da[i-1][ f[i-1][j][k] ][k]
db[i][j][k] = db[i-1][j][k] + db[i-1][ f[i-1][j][k] ][k]
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+5, M = 17;
const long long INF = 1e12;
long long h[N];
typedef long long ll;
typedef pair<ll, int> PLI;
set<pair<ll, int> >s;
ll ga[N], gb[N];
int f[M][N][2];
ll da[M][N][2];
ll db[M][N][2];
void init_g(int n)
{
s.insert(make_pair(INF, 0));
s.insert(make_pair(INF+1, 0));
s.insert(make_pair(-INF, 0));
s.insert(make_pair(-INF-1, 0));
for(int i = n; i; i--)
{
vector<PLI>v;
set<pair<ll, int> >::iterator it = s.lower_bound(make_pair(h[i], i));
it++;
for(int j = 1; j <= 4; j++) {
v.push_back(*it);
it--;
}
ll pmin1 = 0, pmin2 = 0;
ll min1 = INF, min2 = INF;
for(int j = 3; j >= 0; j--)
{
ll val = abs(v[j].first-h[i]);
ll pos = v[j].second;
if(val < min1)
{
min2 = min1;
pmin2 = pmin1;
min1 = val;
pmin1 = pos;
}
else if(val < min2)
{
min2 = val;
pmin2 = pos;
}
}
ga[i] = pmin2, gb[i] = pmin1;//注意可能最后ga[n-1],ga[n],gb[n-1],gb[n]存在问题
s.insert(make_pair((ll)h[i], i));
}
}
void init_f(int n)
{
for(int i = 0; i < M; i++)
{
for(int j = 1; j <= n; j++)
{
if(i == 0)
{
f[0][j][0] = ga[j];
f[0][j][1] = gb[j];
}
else if(i == 1)
{
f[1][j][0] = f[0][ f[0][j][0] ][1];
f[1][j][1] = f[0][ f[0][j][1] ][0];
}
else
{
f[i][j][0] = f[i-1][ f[i-1][j][0] ][0];
f[i][j][1] = f[i-1][ f[i-1][j][1] ][1];
}
}
}
}
inline ll dist(ll a, ll b)
{
return abs(h[a]-h[b]);
}
void init_d(int n)
{
for(int i = 0; i < M; i++)
{
for(int j = 1; j <= n; j++)
{
if(i == 0)
{
da[0][j][0] = dist(j, f[0][j][0]), db[0][j][0] = 0;
db[0][j][1] = dist(j, f[0][j][1]), da[0][j][1] = 0;
}
else
{
for(int k = 0; k < 2; k++)
{
if(i == 1)
{
da[1][j][k] = da[0][j][k] + da[0][ f[0][j][k] ][1-k];
db[1][j][k] = db[0][j][k] + db[0][ f[0][j][k] ][1-k];
}
else{
da[i][j][k] = da[i-1][j][k] + da[i-1][ f[i-1][j][k] ][k];
db[i][j][k] = db[i-1][j][k] + db[i-1][ f[i-1][j][k] ][k];
}
}
}
}
}
}
void calc(ll p, ll x, ll &a, ll &b)
{
a = 0, b = 0;
for(int i = M-1; i >= 0; i--)
{
if(f[i][p][0] && a+b+da[i][p][0]+db[i][p][0] <= x)//从小A开始走
{
a += da[i][p][0];
b += db[i][p][0];
p = f[i][p][0];
}
}
}
int main()
{
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++)
scanf("%lld", h+i);
init_g(n);
//for(int i = 1; i <= n; i++)
// cout << ga[i] << " " << gb[i] << "\n";
init_f(n);
init_d(n);
int x0;
int res = 0;
ll h_max = 0;
double rate_min = INF;//GUG位置:用了ll 。。。。。
scanf("%d", &x0);
for(int i = 1; i <= n; i++)
{
ll aa, bb;
calc(i, x0, aa, bb);
double rate = bb?(double)aa/bb :INF;
if(rate < rate_min || rate == rate_min &&h[i] > h_max)
{
rate_min = rate;
res = i;
h_max = h[i];
}
}
printf("%lld\n", res);
int m;
scanf("%d", &m);
while(m--)
{
ll s, x;
cin >> s >> x;
ll aa, bb;
calc(s, x, aa, bb);
printf("%d %d\n", aa, bb);
}
return 0;
}
AcWing\294. 计算重复
输入样例:
ab 2
acb 4
acb 1
acb 1
aa 1
aaa 3
baab 1
baba 11
aaaaa 1
aaa 20
输出样例:
2
1
4
7
12
这一道题目简单概括就是求一个最大的m,使得字符串s2
被复制m*n2
之后是字符串s1*n1
的子序列。
更进一步进行简化:可以认为求一个最大的t,使得s2复制t次后是s1复制n1次的子序列。
m = ⌊ t n 2 ⌋ m=\lfloor \frac{t}{n2}\rfloor m=⌊n2t⌋.
这是因为满足单调性:当复制t次是子序列,那么当小于t次,显然也是子序列。
当复制t+1次已经不是子序列,那么大于t+1时,同样不再是子序列。(因为有多余的在s1*n1无法找到)
在这里,首先认为s1*INF是无限多的。由于具有周期性,可以使用0—size-1的位置表示。
f[i][j]
.指从第i个位置开始,得到 2 j 2^j 2j个重复的s2的子序列所需要的最短的长度。
值得一提的是要注意有无解的情况
#include <bits/stdc++.h>
using namespace std;
char s1[120], s2[120];
int n1, n2;
typedef long long ll;
#define M 31
ll f[120][M];
int main()
{
while(scanf("%s%d%s%d", s2, &n2, s1, &n1) != EOF)//由于具有取模操作,所以两个字符串从0开始计算
{
//暴力枚举f[i][0]
bool fault = false;//无解情况
int len1 = strlen(s1), len2 = strlen(s2);
for(int i = 0; i < len1; i++)
{
f[i][0] = 0;
int p = i;//在s1中的位置指针
for(int j = 0; j < len2; j++)
{
int cnt = 0;
while(s1[p] != s2[j])
{
p = (p+1)%len1;
cnt++;
if(cnt >= len1) {
fault = true; break;}
}
if(fault) break;
f[i][0] += 1+cnt;
p = (p+1)%len1;
}
if(fault) break;
}
if(fault){
puts("0");
continue;
}
//for(int i = 0; i < len1; i++) printf("%d\t", f[i][0]);
//初始化倍增DP
for(int j = 1; j < M; j++)
{
for(int i = 0; i < len1; i++)
{
f[i][j] = f[i][j-1] + f[ (i+f[i][j-1])%len1 ][j-1];
}
}
ll ans = 0;
//进行拼凑
for(int i = 0; i < len1; i++)
{
ll t = 0, p = i;
for(int j = M-1; j >= 0; j--)
{
if(p + f[p%len1][j] > len1*n1) continue;
p += f[p%len1][j];
t += 1 << j;
}
ans = max(ans, t);
}
printf("%d", ans/n2);
}
return 0;
}
/* 格外注意:我的f[][]数组的第一位的取值范围仅仅可以是0~len-1 */