SDUT 2022 Spring Individual Contest - K (个人赛补题)
G - Ganyu Segment Tree
题意:
有三种操作
每一个都有两种状态,可修改状态和不可修改状态
\(op1\),把\(a_p\)的状态修改为另一个状态
\(op2\),\([l,r]\)区间内的所有可修改数都\(×X\)
\(op3\) ,询问\([l,r]\)区间内的所有数能否整除\(X\),如果可以整除就把\([l,r]\)所有可修改都除以\(X\)
思路:
修改的数\(X\)很小,所以可以把\(X\)质因数分解,\([1,30]\)以内就\(10\)个质数,对于每个质数都开一个线段树,维护区间内当前质因子的个数,能否整除就看区间内当前质因子个数的最小值是否小于当前询问的\(X\)的质因子,只要区间最小值\(≥\)当前质因子个数,就再进行区间修改。
对于两种状态,就在线段树开两个值\(x,y\)f分别代表当前质因子区间个数的最小值,区间不可修改的数的质因子个数的最小值,对于第一个操作,就只需要把\(swap(x,y)\),每次查询的时候,就对区间的\((x,y)\)取最小值,每次修改操作不动区间内的\(y\)值,就是带质因数分解的区间修改区间查询的线段树。
View Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int inf = 1e18;
const int N = 1e5 + 10;
// 2 3 5 7 11 13 17 19 23 29
struct node {
int l, r;
int s1, s2;
int add;
} tr[N * 4][12];
int n, m;
int a[13] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
void push_up(int u, int id) {
tr[u][id].s1 = min(tr[u << 1][id].s1, tr[u << 1 | 1][id].s1);
tr[u][id].s2 = min(tr[u << 1][id].s2, tr[u << 1 | 1][id].s2);
}
void push_down(int u, int id) {
int add = tr[u][id].add;
if (tr[u][id].add) {
tr[u << 1][id].s1 += add, tr[u << 1 | 1][id].s1 += add;
tr[u << 1][id].add += add, tr[u << 1 | 1][id].add += add;
tr[u][id].add = 0;
}
}
void build(int u, int l, int r, int id) {
if (l == r) {
tr[u][id] = {l, r, 0, inf, 0};
} else {
tr[u][id] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid, id);
build(u << 1 | 1, mid + 1, r, id);
push_up(u, id);
}
}
void modify(int u, int l, int r, int x, int id) {
if (tr[u][id].l >= l && tr[u][id].r <= r) {
tr[u][id].s1 += x;
tr[u][id].add += x;
} else {
push_down(u, id);
int mid = tr[u][id].l + tr[u][id].r >> 1;
if (l <= mid) modify(u << 1, l, r, x, id);
if (r > mid) modify(u << 1 | 1, l, r, x, id);
push_up(u, id);
}
}
int query(int u, int l, int r, int id) {
if (tr[u][id].l >= l && tr[u][id].r <= r) {
return min(tr[u][id].s1, tr[u][id].s2);
} else {
push_down(u, id);
int mid = tr[u][id].l + tr[u][id].r >> 1;
int res = inf;
if (l <= mid) res = min(res, query(u << 1, l, r, id));
if (r > mid) res = min(res, query(u << 1 | 1, l, r, id));
return res;
}
}
void change(int u, int pos, int id) {
if (tr[u][id].l == tr[u][id].r && tr[u][id].l == pos) {
swap(tr[u][id].s1, tr[u][id].s2);
} else {
push_down(u, id);
int mid = tr[u][id].l + tr[u][id].r >> 1;
if (pos <= mid)
change(u << 1, pos, id);
else
change(u << 1 | 1, pos, id);
push_up(u, id);
}
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
int l, r, x;
for (int i = 1; i <= 10; i++) {
build(1, 1, n, i);
}
while (m--) {
string s;
cin >> s;
if (s == "mul") {
cin >> l >> r >> x;
for (int i = 1; i <= 10; i++) {
int cnt = 0;
if (x % a[i] == 0) {
while (x % a[i] == 0) {
cnt++;
x /= a[i];
}
modify(1, l, r, cnt, i);
}
}
} else if (s == "flip") {
int p;
cin >> p;
for (int i = 1; i <= 10; i++) {
change(1, p, i);
}
} else {
cin >> l >> r >> x;
if (x == 1) {
cout << "YES" << endl;
continue;
}
bool flag = 1;
int now=x;
for (int i = 1; i <= 10; i++) {
int cnt = 0;
if (x % a[i] == 0) {
while (x % a[i] == 0) {
cnt++;
x /= a[i];
}
if (query(1, l, r, i) < cnt) {
flag = 0;
}
}
}
if (flag == 0) {
cout << "NO" << endl;
} else {
x=now;
cout << "YES" << endl;
for (int i = 1; i <= 10; i++) {
int cnt = 0;
if (x % a[i] == 0) {
while (x % a[i] == 0) {
cnt++;
x /= a[i];
}
modify(1, l, r, -cnt, i);
}
}
}
}
}
}
E - Easy Problem
题意:
给一张\(N*M\)的地图,有障碍物,现在给定两个机器人的坐标位置,只有上下左右的指令,这个指令只针对两个机器人同时进行的(同上下左右),问两个机器人相遇的最少操作数
思路:
由于地图范围很小,所以直接开四维,直接把两个点的坐标都存进去,\(dis(x1,y1,x2,y2)\)代表\(a\)在\((x1,y1)\),\(b\)在\((x2,y2)\)的这种状态的最小操作数,所以最终对\(dis(x,y,x,y)\)取最小值就行
View Code
#include <bits/stdc++.h>
using namespace std;
const int N = 60;
const int inf = 0x3f3f3f3f;
typedef pair<int, int> PII;
#define x first
#define y second
char s[N][N];
int n;
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
PII st, ed;
int dis[N][N][N][N];
int vis[N][N][N][N];
struct node {
int a, b, c, d;
};
void bfs(PII st, PII ed) {
memset(dis, 0x3f, sizeof dis);
dis[st.x][st.y][ed.x][ed.y] = 0;
queue<node> q;
q.push({st.x, st.y, ed.x, ed.y});
while (q.size()) {
auto t = q.front();
q.pop();
int a = t.a, b = t.b, c = t.c, d = t.d;
if (vis[a][b][c][d]) continue;
vis[a][b][c][d] = 1;
for (int i = 0; i < 4; i++) {
int na = a + dx[i];
int nb = b + dy[i];
int nc = c + dx[i];
int nd = d + dy[i];
if (na < 1) na = 1;
if (nb < 1) nb = 1;
if (nc < 1) nc = 1;
if (nd < 1) nd = 1;
if (na > n) na = n;
if (nb > n) nb = n;
if (nc > n) nc = n;
if (nd > n) nd = n;
if (s[na][nb] == '*') na = a, nb = b;
if (s[nc][nd] == '*') nc = c, nd = d;
if (dis[na][nb][nc][nd] > dis[a][b][c][d] + 1) {
q.push({na, nb, nc, nd});
dis[na][nb][nc][nd] = dis[a][b][c][d] + 1;
}
}
}
}
signed main() {
cin >> n;
for (int i = 1; i <= n; i++) {
scanf("%s", s[i] + 1);
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (s[i][j] == 'a') {
st = {i, j};
}
if (s[i][j] == 'b') {
ed = {i, j};
}
}
}
bfs(st, ed);
int minv = inf;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
minv = min(minv, dis[i][j][i][j]);
}
}
if (minv == inf)
cout << "no solution" << endl;
else
cout << minv << endl;
}
J - IHI's Magic String
题意:
有三种操作
一,在一个字符串的末尾添加一个字符
二,删除末尾的一个字符(没字符就不操作)
三,使得所有的\(a\)字符都变成\(b\)字符
问操作完后,最终形成的字符串是是什么
思路:
先把所有操作都存在来,然后从后往前遍历,开个\(del\),表示需要删除多少个字符,每次遇见操作二\(+1\),每次遇到操作一,若没有\(del\)则添加字符,否则就\(del-1\)。开个\(fa(i)\)数组,每个颜色指向自己,每次遇见操作三,就使得颜色\(a\)需要指向颜色\(b\)所指向的。
View Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
struct node {
int op;
char x, y;
} a[N];
int n;
int fa[N];
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
string s;
cin >> n;
for (int i = 0; i < 26; i++) {
fa[i] = i;
}
for (int i = 1; i <= n; i++) {
cin >> a[i].op;
if (a[i].op == 1) {
cin >> a[i].x;
} else if (a[i].op == 3) {
cin >> a[i].x >> a[i].y;
}
}
int bks = 0;
for (int i = n; i >= 1; i--) {
if (a[i].op == 1) {
if (bks > 0) {
bks--;
} else {
char x = fa[a[i].x - 'a'] + 'a';
s.push_back(x);
}
} else if (a[i].op == 2) {
bks++;
} else {
fa[a[i].x - 'a'] = fa[a[i].y - 'a'];
}
}
if (s.empty()) {
cout << "The final string is empty" << endl;
} else {
reverse(s.begin(), s.end());
cout << s << endl;
}
}
H - Optimal Biking Strategy
题意:
有\(N\)个自行车站,每次骑车和放车都必须在自行车站进行(很重要),现在给你\(N\)个自行车站的位置,起点在\(0\),终点在\(p\),每耗\(1\)元钱,就可以骑车\(s\)公里,不满一公里,也消耗\(1\)元钱,现在给定\(s\)元,问你最少步行多少公里。
思路:
\(dp(i,j)\)代表考虑前\(i\)个车站,使用了\(j\)元,最多骑行多少公里。
现在用了\(j-k\)元,所以二分找到大于等于当前点往前\((j-k)*s\)的车站位置,为\(now\)
\(dp(i,j)=max(dp(i,j),dp(now,k)+a_i-a_{now})\),代表在\(now\)位置使用\(j-k\)元,骑到当前位置
\(dp(i,j)=max(dp(i-1,j))\),现在不用钱
View Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;
int n, p, s, m;
int a[N];
int dp[N][6];
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(0) ;
cout.tie(0) ;
cin >> n >> p >> s;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
cin >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
for (int k = 0; k < j; k++) {
int dis = s * (j - k);
dis = a[i] - dis;
int now = lower_bound(a+1, a + n + 1, dis) - a;
dp[i][j] = max({dp[i][j], dp[now][k] + a[i] - a[now]});
}
dp[i][j] = max({dp[i][j], dp[i - 1][j]});
}
}
int ans=0;
for(int i=1;i<=n;i++) {
ans=max(ans,dp[i][m]);
}
cout<<p-ans<<endl;
}

浙公网安备 33010602011771号