旅行傳送門
A - Exact Price
AC代碼:
#include <bits/stdc++.h>
int main()
{
int n;
scanf("%d", &n);
puts((n && (n % 100 == 0)) ? "Yes" : "No");
return 0;
}
B - String Shifting
題意:給你一個字符串 \(S\) ,你可以進行下列操作之一任意次:
- 將第一個字符移至字符串末尾
- 將最后一個字符移至字符串開頭
求可以得到的字典序最小與最大的字符串。
題目分析:字典序最小顯然是以字符串中最小的字符開頭,因此我們可以用一個數組 \(pos\) 存下字符串中最小字符出現的位置然后遍歷,交換 \(S\) 中 \(1\) ~ \(pos_{i-1}\) 和 \(pos_i\) ~ \(len\) 這兩段后更新答案即可,反之亦然。
AC代碼:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
#define pdd pair<double, double>
#define IOS \
ios::sync_with_stdio(false); \
cin.tie(nullptr); \
cout.tie(nullptr);
using namespace std;
int main(int argc, char const *argv[])
{
string s;
cin >> s;
vector<int> pos[100];
rep(i, 0, s.length()) pos[s[i] - 'a'].push_back(i);
int mn, mx;
rep(i, 0, 25)
{
if (pos[i].size())
{
mn = i;
break;
}
}
down(i, 25, 0)
{
if (pos[i].size())
{
mx = i;
break;
}
}
string ans = s;
for (auto idx : pos[mn])
{
string t1 = s.substr(0, idx);
string t2 = s.substr(idx, s.length() - idx);
ans = std::min(ans, t2 + t1);
}
cout << ans << endl;
ans = s;
for (auto idx : pos[mx])
{
string t1 = s.substr(0, idx);
string t2 = s.substr(idx, s.length() - idx);
ans = std::max(ans, t2 + t1);
}
cout << ans << endl;
return 0;
}
C - Doukasen
題意:有一串保險絲,每根保險絲的長度和燃燒速度不同,現同時從左右端點燃,問火苗相遇時的位置與最左端的距離。
題目分析:雙指針模擬。
AC代碼:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
#define pdd pair<double, double>
#define IOS \
ios::sync_with_stdio(false); \
cin.tie(nullptr); \
cout.tie(nullptr);
using db = double;
using namespace std;
int main(int argc, char const *argv[])
{
IOS;
int n;
cin >> n;
std::vector<pdd> a(n + 1);
rep(i, 1, n) cin >> a[i].first >> a[i].second;
db ans = 0;
int l = 1, r = n;
while (l < r)
{
db t1 = a[l].first / a[l].second;
db t2 = a[r].first / a[r].second;
if (t1 < t2)
{
ans += a[l].first;
a[r].first -= t1 * a[r].second;
++l;
}
else if (t1 > t2)
{
ans += t2 * a[l].second;
a[l].first -= t2 * a[l].second;
--r;
}
else
{
ans += a[l].first;
++l, --r;
}
}
if (l == r)
ans += a[l].first / 2;
cout << fixed << setprecision(15) << ans << endl;
return 0;
}
D - Restricted Permutation
題意:給你兩個序列 \(A\) 與 \(B\) ,要你構造出滿足下列條件的序列 \(P\) :
- 對每個下標 \(i\) , \(A_i\) 在 \(P\) 中的位置比 \(B_i\) 靠前
- 字典序最小
題目分析:首先考慮什么時候不存在這樣的序列 \(P\) ?不難發現,\(AB\) 中元素的出現順序是具有“傳遞性”的,舉栗子的話就是,假設有 \(i,j \in (\mathcal{1,2…n})\) ,存在 \(A_i = a , A_j = b\) 與 \(B_i = b , B_j = c\) ,即存在 \(a < b\) 與 \(b < c\) (這里的 \(<\) 代表出現位置更靠前),因此可得 \(a < c\) ,所以若是傳遞過程中出現矛盾(既有 \(a < c\) 又有 \(c < a\) )則序列不存在。
那如何判斷有沒有矛盾呢?對每組 \(\{A_i,B_i\} = \{x,y\}\) ,我們不妨由 \(x\) 向 \(y\) 連一條有向邊,代表 \(x < y\) ,最好連出來的圖中如果存在環,說明存在矛盾。那怎么去環呢?拓撲排序,至此整個題目的解法也就水落石出了,字典序最小只需用優先隊列維護即可。
AC代碼:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
const int maxn = 2e5 + 5;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
int n, m;
int in[maxn];
std::vector<int> ans, e[maxn];
std::priority_queue<int, std::vector<int>, std::greater<int>> q;
int main(int argc, char const *argv[])
{
n = read(), m = read();
rep(i, 1, m)
{
int u = read(), v = read();
++in[v], e[u].push_back(v);
}
rep(i, 1, n) if (!in[i]) q.push(i);
int cnt = n;
while (!q.empty())
{
int u = q.top();
q.pop();
--cnt;
ans.push_back(u);
for (auto v : e[u])
if (!--in[v])
q.push(v);
}
if (cnt)
printf("-1");
else
for (auto x : ans)
printf("%d ", x);
puts("");
return 0;
}
E - Placing Rectangles
題意:給你在直角坐標系中划分出一塊長為 \(X\) ,寬為 \(Y\) 的矩形區域,現給你三個面積至少為 \(A、B、C\) 的矩形,問能否在這片矩形區域中不重疊地放置這三個矩形。
題目分析:首先我們考慮只放置兩個矩形的情況,設兩個矩形的面積至少為 \(S\) 和 \(T\) :
對於所有合法的放置,一定存在一條平行於 \(x\) 軸或 \(y\) 軸的直線 \(l\) ,且 \(l\) 滿足以下條件:
- 不會穿過任何一個矩形
- 將矩形區域拆分為了兩部分,每一部分都能放置一個矩形
如果該直線 \(l\) 存在,且與 \(x\) 軸平行,則 \(l\) 的取值 \(y=⌈\frac{S}{X}⌉\)
如果該直線 \(l\) 存在,且與 \(y\) 軸平行,則 \(l\) 的取值 \(x=⌈\frac{S}{Y}⌉\)
只需判斷這兩種情況,即可得出答案。
那么當我們要放置三個矩形時,實際上就是換湯不換葯了,不妨對拆分出的兩部分區域任選一份繼續拆分,再次判斷上述兩種情況,這樣要放置四個五個 \(n\) 個矩形都是同樣的做法。
AC代碼:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
using ll = long long;
bool solve2(ll x, ll y, ll s, ll t)
{
rep(i, 1, 2)
{
ll len = (s - 1) / x + 1;
if (len < y && x * (y - len) >= t)
return true;
std::swap(x, y);
}
return false;
}
bool solve1(ll x, ll y, ll a, ll b, ll c)
{
rep(i, 1, 2)
{
rep(j, 1, 3)
{
ll len = (a - 1) / x + 1;
if (len < y && solve2(x, y - len, b, c))
return true;
std::swap(a, b), std::swap(b, c);
}
std::swap(x, y);
}
return false;
}
int main(int argc, char const *argv[])
{
ll x, y, a, b, c;
scanf("%lld %lld %lld %lld %lld", &x, &y, &a, &b, &c);
puts(solve1(x, y, a, b, c) ? "Yes" : "No");
return 0;
}
F - Parenthesis Checking
題意:已知一個由 \(N\) 個左右括號組成的字符串 \(S\) ,你需要進行下面兩種操作:
- \(1~~l~~r\) :交換 \(S\) 的第 \(l\) 和第 \(r\) 個字符
- \(2~~l~~r\) :判斷區間 \([l,r]\) 內的括號序列是否合法
題目分析:線段樹好題。
首先我們考慮如何判斷某個長為 \(M\) 的括號序列是否合法:設 ‘(’ 為 \(+1\) ,‘)’ 為 \(-1\) ,然后對序列作前綴和 \(sum\) ,這樣當且僅當 \(sum_M = 0\) 且 \(sum\) 的最小元素為 \(0\) 時序列才合法。(如 )( 這種情況區間和雖然為 \(0\) ,但是並不合法)
線段樹的每個節點維護區間和 \(sum\) 與前綴和最小值 \(mn\) 。我們可以寫 \(pushup\) 函數來合並兩條線段:
#define lson k << 1
#define rson k << 1 | 1
void pushup(int k)
{
tree[k].sum = tree[lson].sum + tree[rson].sum;
tree[k].mn = std::min(tree[lson].mn, tree[lson].sum + tree[rson].mn);
}
為什么這樣維護呢?這個問題我想了一下午,按理來說應該直接比較左右子樹的前綴和最小值啊,但我們要明確的一點是:我們維護的 \(mn\) ,是前綴和的歷史最小值。不妨將左右子樹分別看作是某個區間的前后半段,這樣后半段每有一對諸如 )( 的不合法括號時,就會使得其前綴和最小值 \(-1\) 。但是當前半段的 ‘(’ 有冗余,即 \(sum[lson] > 0\) 時,就可以和后半段無法匹配的 ‘)’ 結合成一對合法的序列從而對后半段的歷史最小值產生影響,使得 \(mn+1\) 。因此合並時需要比較左子樹的前綴和最小值與左子樹的區間和 \(+\) 右子樹的前綴和最小值,用較小的那個更新 \(mn\) 。
AC代碼:
#include <bits/stdc++.h>
#define rep(i, x, y) for (register int i = (x); i <= (y); i++)
#define down(i, x, y) for (register int i = (x); i >= (y); i--)
const int maxn = 2e5 + 5;
char buf[1 << 23], *p1 = buf, *p2 = buf, obuf[1 << 23], *O = obuf;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
inline int read()
{
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
int n, q, a[maxn];
#define lson k << 1
#define rson k << 1 | 1
struct node
{
int l, r, sum, mn;
} tree[maxn << 2];
void pushup(int k)
{
tree[k].sum = tree[lson].sum + tree[rson].sum;
tree[k].mn = std::min(tree[lson].mn, tree[lson].sum + tree[rson].mn);
}
void build(int k, int l, int r)
{
tree[k].l = l, tree[k].r = r;
if (l == r)
{
tree[k].sum = tree[k].mn = a[l];
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
pushup(k);
}
void update(int k, int p)
{
if (tree[k].l == tree[k].r && tree[k].l == p)
{
tree[k].sum = tree[k].mn = a[p];
return;
}
int mid = (tree[k].l + tree[k].r) >> 1;
p <= mid ? update(lson, p) : update(rson, p);
pushup(k);
}
node query(int k, int l, int r)
{
if (l <= tree[k].l && tree[k].r <= r)
return tree[k];
int mid = (tree[k].l + tree[k].r) >> 1;
if (r <= mid)
return query(lson, l, r);
else if (l > mid)
return query(rson, l, r);
else
{
node res, lft, rht;
lft = query(lson, l, mid), rht = query(rson, mid + 1, r);
res.sum = lft.sum + rht.sum;
res.mn = std::min(lft.mn, lft.sum + rht.mn);
return res;
}
}
int main(int argc, char const *argv[])
{
n = read(), q = read();
rep(i, 1, n) a[i] = (getchar() == '(' ? 1 : -1);
build(1, 1, n);
int opt, l, r;
while (q--)
{
opt = read(), l = read(), r = read();
if (opt == 1)
{
if (a[l] == a[r])
continue;
std::swap(a[l], a[r]);
update(1, l), update(1, r);
}
else
{
node res = query(1, l, r);
if (res.sum == 0 && res.mn == 0)
puts("Yes");
else
puts("No");
}
}
return 0;
}