C
题意
给定两个数组a,b,以及一个正整数m, 可以对a进行若干次两种操作
- 选择一个长度为m且全部数字相同的子数组,假设数字为x,则将它们合并为一个数m*x。
- 选择a中一个m的倍数x,将其分割成m个m/x。顺序保持和原来一致。
问能否将a变成b数组
思路
很简单的想到操作1 、 2两种操作是可逆的,但是 4 8 4 变成 16这样的操作我们难以模拟,不妨就让a, b数组一起进行操作2 看最后的数组一不一样即可
代码实现
唯一一个难点就是数组进行操作二后可能非常大,我们考虑用pair<int,longlong> 【x, v】来存储连续的 x 的个数 v
vector<PII> divide (vector<int> p, int m)
{
vector<PII> res;
int len = p.size();
for (int i = 1 ; i < len; i ++)
{
if (p[i] % m == 0)
{
int k = p[i];
int cnt = 1;
while (k % m == 0)
{
k /= m;
cnt *= m;
}
res.push_back ({k, cnt});
}
else res.push_back ({p[i], 1});
}
return res;
}
vector<PII> merge (vector<PII> p)
{
vector<PII> res;
int last = -1;
for (int i = 0; i < p.size(); i ++)
{
if (p[i].x == last) res[res.size() - 1].y += p[i].y;
else res.push_back ({p[i].x, p[i].y});
last = p[i].x;
}
return res;
}
void solve()
{
int n1, n2, m;
cin >> n1 >> m;
vector<int> a (n1 + 1);
for (int i = 1 ; i <= n1 ; i ++)
cin >> a[i];
cin >> n2 ;
vector<int> b (n2 + 1);
for (int i = 1 ; i <= n2 ; i ++)
cin >> b[i];
auto A = divide (a, m);// divide 表示将数组进行操作2
auto B = divide (b, m);
A = merge (A); B = merge (B);// merge 表示将数组进行合并
if (A.size() == B.size())
{
bool ok = true;
int len = A.size();
for (int i = 0; i < len ; i ++)
if (A[i].x != B[i].x or A[i].y != B[i].y)
ok = false;
if (ok) cout << "Yes" << endl;
else cout << "No" << endl;
return ;
}
cout << "No" << endl;
}
D
题意
给定一个排列,选取一个区间 l, r 如果 [ l , r ] 区间中左右两个端点是这个区间的最大和最小值,那么给l , r连一条边权为1 的边 ,问建边完后从1走到n的最短距离是多少?
思路
建图是陷阱,n方的建图完全不行,只需要看怎么从1走到n即可,我们可以发现整个区间的最大和最小值一定会成为路径上的某个点,
这个题就变成了选最少的点从1跳到n,其中最大最小值一定要选。
以最小值为例:最小值左右分成两个区间分别需要这两个分区间的最大值来作为跳板,在分治下去,直到1和n连通。
需要完成这样的操作,我们需要预处理前缀最小值,最大值,后缀最小值,最大值
代码实现
void solve()
{
int n;
cin >> n;
vector<int> a (n);
for (int i = 0; i < n ; i ++)
cin >> a[i];
int mi = min_element (a.begin(), a.end()) - a.begin();
vector<int> lmin (n), lmax (n), rmin (n), rmax (n);
lmin[0] = rmin[0] = 0;
for (int i = 1 ; i < n ; i ++)
{
if (a[i] < a[lmin[i - 1]]) lmin[i] = i;
else lmin[i] = lmin[i - 1];
if (a[i] > a[lmax[i - 1]]) lmax[i] = i;
else lmax[i] = lmax[i - 1];
}
rmax[n - 1] = rmin[n - 1] = n - 1;
for (int i = n - 2 ; i >= 0; i --)
{
if (a[i] < a[rmin[i + 1]]) rmin[i] = i;
else rmin[i] = rmin[i + 1];
if (a[i] > a[rmax[i + 1]]) rmax[i] = i;
else rmax[i] = rmax[i + 1];
}
int id = mi;
int res = 0;
while (id)
{
id = lmax[id];
res ++;
if (!id) break;
id = lmin[id];
res ++;
}
id = mi;
while (id < n - 1)
{
id = rmax[id];
res ++;
if (id >= n - 1) break;
id = rmin[id];
res ++;
}
cout << res << endl;
}