2020杭電多校第三場題解
阿新 • • 發佈:2020-07-29
2020 Multi-University Training Contest 3
施工中。。。
1004 Tokitsukaze and Multiple
#include<bits/stdc++.h> #define ll long long #define maxn 100010 #define mod 998244353 using namespace std; int a[maxn], sum[maxn], dp[maxn], las[maxn]; int main() { int t; scanf("%d", &t); while (t--) { int n, p; scanf("%d%d", &n, &p); int ans = 0; for (int i = 0; i <= p; i++) las[i] = 0; for (int i = 1; i <= n; i++) { int x; scanf("%d", &x); x %= p; a[i] = x; } for (int i = 1; i <= n; i++) sum[i] = (sum[i - 1] + a[i]) % p; for (int i = 1; i <= n; i++) { dp[i] = dp[i - 1]; if (las[sum[i]] == 0 && sum[i] != 0) dp[i] = max(dp[i], dp[las[sum[i]]]); else dp[i] = max(dp[i], dp[las[sum[i]]] + 1); las[sum[i]] = i; ans = max(ans, dp[i]); } printf("%d\n", ans); } return 0; }
1005 Little W and Contest
#include<bits/stdc++.h> #define ll long long #define maxn 100010 #define mod 1000000007 using namespace std; ll f[maxn], s1[maxn], s2[maxn]; int fi(int x) { if (f[x] == x) return x; f[x] = fi(f[x]); return f[x]; } ll po(ll x) { ll bas = 1, y = mod - 2; while (y) { if (y % 2) bas = bas * x % mod; x = x * x % mod; y /= 2; } return bas; } int main() { int t; scanf("%d", &t); while (t--) { int n; scanf("%d", &n); ll sum1 = 0, sum2 = 0; for (int i = 1; i <= n; i++) { int x; scanf("%d", &x); f[i] = i, s1[i] = s2[i] = 0; if (x == 1) s1[i]++, sum1++; else s2[i]++, sum2++; } ll ans = 0; ans = sum1 * (sum2 * (sum2 - 1) / 2) % mod % mod + (sum2 * (sum2 - 1) / 2 * (sum2 - 2) / 3) % mod; ans %= mod; vector<ll>v; v.push_back(ans); ll su2 = sum2 * (sum2 - 1) / 2 % mod; ll p2 = po(2), p6 = po(6); for (int i = 1; i < n; i++) { int x, y; scanf("%d%d", &x, &y); x = fi(x), y = fi(y); ans -= (sum1 - s1[x] - s1[y]) * s2[x] % mod * s2[y] % mod; ans %= mod; ans -= (sum2 - s2[x] - s2[y]) * s1[x] % mod * s2[y] % mod; ans %= mod; ans -= (sum2 - s2[x] - s2[y]) * s2[x] % mod * s1[y] % mod; ans %= mod; ans -= (sum2 - s2[x] - s2[y]) * s2[x] % mod * s2[y] % mod; ans %= mod; while (ans < 0) ans += mod; su2 -= s2[x] * s2[y] / 2; su2 %= mod; while (su2 < 0) su2 += mod; s1[x] += s1[y]; s2[x] += s2[y]; f[y] = x; v.push_back(ans); } for (int i = 0; i < v.size(); i++) printf("%lld\n", v[i]); } return 0; }
1006 X Number
\(dp[pos][cnt][num]\) \(pos\) 為剩餘的位數、\(cnt\) 為前面位數的組合情況、\(num\) 為要搜尋的數的個數
本題要查詢 \(0-9\) 每一位的位數,我們將 \(19\) 位分成若干個 \(0-9\) ,如\(\{(13,0),(2,1),(2,3),(1,4),(1,5),(0,6),(0,7),(0,8),(0,9)\}\),大概有幾千萬種情況
事實上 \(0-9\) 對於我們來說只有等於 \(d\) 和不等於 \(d\) 的區別,那麼僅把 \(19\) 個數分成若干塊,如 \(\{13,2,2,1,1,0,0,0,0,0\}\),只有約 \(2000\)
那麼我們用 map<vector<int>, cnt>
來儲存劃分的情況,既可以在複雜度允許的情況,來表示劃分。
接下來,用 \(num\) 表示要搜尋的數的個數,唯一標識劃分的情況
最後,數位 \(dp\) 暴力轉移即可
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2005;
const int base = 25;
int Case = 0;
int g;
vector<int> tmp, E[maxn];
map<vector<int>, int> Map;
void init(int now, int k) {
if (now == 10) {
Map[tmp] = ++g;
E[g] = tmp;
return;
}
int top = k;
if (now)
top = min(top, tmp[now - 1]);
for (int i = 0; i <= top; i++) {
tmp.push_back(i);
init(now + 1, k - i);
tmp.pop_back();
}
}
int a[base], len, d;
LL dp[base][maxn][base];
int limit_v[15], limit_cnt[base];
vector<int> limit_vc[base];
LL dfs(int pos, int cnt, int num, bool lead, bool limit) {
if (pos > len) {
if (E[cnt][0] > E[cnt][1] && E[cnt][0] == num) {
return 1;
} else
return 0;
}
if (dp[len - pos + 1][cnt][num] != -1 && (!lead) && (!limit))
return dp[len - pos + 1][cnt][num];
LL res = 0;
int top = limit ? a[len - pos + 1] : 9;
if (lead) {
res += dfs(pos + 1, cnt, num, lead, false);
int top3 = top;
if (limit)
top3--;
for (int i = 1; i <= top3; i++) {
vector<int> tmp(10, 0);
tmp[0] = 1;
res += dfs(pos + 1, Map[tmp], num + (d == i), false, false);
}
if (limit)
res += dfs(pos + 1, limit_cnt[pos], num + (d == top), false, true);
} else if (limit) {
vector<int> vc(10, 0);
for (int i = 1; i < pos; i++)
vc[a[len - i + 1]]++;
for (int i = 0; i < top; i++) {
vector<int> temp = vc;
temp[i]++;
sort(temp.begin(), temp.end(), [](const int a, const int b) {
return a > b;
});
res += dfs(pos + 1, Map[temp], num + (d == i), false, false);
}
res += dfs(pos + 1, limit_cnt[pos], num + (d == top), false, true);
} else {
bool check = true;
int add = 0;
for (int i = 0; i < 10; i++) {
vector<int> temp = E[cnt];
if (check && temp[i] == num) {
check = false;
add = 1;
} else
add = 0;
temp[i]++;
sort(temp.begin(), temp.end(), [](const int a, const int b) {
return a > b;
});
res += dfs(pos + 1, Map[temp], num + add, false, false);
}
}
if ((!lead) && (!limit))
dp[len - pos + 1][cnt][num] = res;
return res;
}
LL doit(LL x) {
len = 0;
memset(a, 0, sizeof(a));
while (x) {
a[++len] = x % 10;
x /= 10;
}
memset(limit_v, 0, sizeof(limit_v));
for (int i = 1; i <= len; i++) {
limit_v[a[len - i + 1]]++;
limit_vc[i].clear();
for (int j = 0; j <= 9; j++)
limit_vc[i].push_back(limit_v[j]);
sort(limit_vc[i].begin(), limit_vc[i].end(), [](const int a, const int b) {
return a > b;
});
limit_cnt[i] = Map[limit_vc[i]];
}
return dfs(1, 1, 0, true, true);
}
int main() {
init(0, 19);
memset(dp, -1, sizeof(dp));
int t;
scanf("%d", &t);
while (t--) {
Case++;
LL left, right;
scanf("%lld%lld", &left, &right);
scanf("%d", &d);
printf("%lld\n", doit(right) - doit(left - 1));
}
return 0;
}
1007 Tokitsukaze and Rescue
#include<bits/stdc++.h>
#define ll long long
#define maxn 100010
#define mod 1000000007
using namespace std;
int n, k;
int mp[60][60];
int ans;
struct cv {
int x, y;
friend bool operator<(cv p, cv q) {
return p.y > q.y;
}
}dd, bb;
int las[60], len[60];
void sol(int x) {
for (int i = 1; i <= n; i++) {
las[i] = 0, len[i] = 1e8;
}
len[1] = 0;
dd.x = 1, dd.y = 0;
priority_queue<cv>q;
q.push(dd);
while (!q.empty()) {
dd = q.top();
q.pop();
if (dd.x == n) break;
if (dd.y != len[dd.x]) continue;
for (int i = 1; i <= n; i++) {
if (i == dd.x) continue;
if (dd.y + mp[dd.x][i] < len[i]) {
las[i] = dd.x;
len[i] = dd.y + mp[dd.x][i];
bb.x = i, bb.y = len[i];
q.push(bb);
}
}
}
if (x == k) {
ans = max(ans, len[n]);
return;
}
int tp = n;
vector<int>v;
while (tp != 0) {
v.push_back(tp);
tp = las[tp];
}
for (int i = 0; i < v.size() - 1; i++) {
int t1 = v[i], t3 = v[i + 1];
int y = mp[t1][t3];
mp[t1][t3] = mp[t3][t1] = 1e8;
sol(x + 1);
mp[t1][t3] = mp[t3][t1] = y;
}
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &k);
for (int i = 0; i < n * (n - 1) / 2; i++) {
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
mp[x][y] = mp[y][x] = z;
}
ans = 0;
sol(0);
printf("%d\n", ans);
}
return 0;
}
1008 Triangle Collision
二分時間。將正三角形展開,鋪滿整個平面,正三角形內的反射可以看作射線與展開後的正三角形平面的所有交點(如下圖)。我們發現一共只有三種平行的直線,只要分別求出這條射線和這三種直線的交點分佈就能 \(O(1)\) \(\text{check}\) 了。
#include <bits/stdc++.h>
#define db double
using namespace std;
const db eps = 1e-5;
const db pi = acos(-1.0);
int sign(db k) {
if (k > eps) return 1;
else if (k < -eps) return -1;
return 0;
}
int cmp(db k1, db k2) { return sign(k1 - k2); }
struct point {
db x, y;
point() {}
point(db x_, db y_) :x(x_), y(y_) {}
point operator + (const point& k) const { return point(k.x + x, k.y + y); }
point operator - (const point& k) const { return point(x - k.x, y - k.y); }
point operator * (db k) const { return point(x * k, y * k); }
point operator / (db k1) const { return point(x / k1, y / k1); }
point turn(db k1) { return point(x * cos(k1) - y * sin(k1), x * sin(k1) + y * cos(k1)); } // 逆時針旋轉
point turn90() { return point(-y, x); } // 逆時針方向旋轉 90 度
db len() { return sqrt(x * x + y * y); } // 向量長度
db len2() { return x * x + y * y; } // 向量長度
db dis(point rhs) { return ((*this) - rhs).len(); }
point unit() { db d = len(); return point(x / d, y / d); }
bool operator < (const point& k) const {
return x == k.x ? y < k.y : x < k.x;
}
bool getP() const { return sign(y) == 1 || (sign(y) == 0 && sign(x) == -1); }
}triangleVertex[3];
db cross(point k1, point k2) { return k1.x * k2.y - k1.y * k2.x; }
db dot(point k1, point k2) { return k1.x * k2.x + k1.y * k2.y; }
db rad(point k1, point k2) { return atan2(cross(k1, k2), dot(k1, k2)); }
int compareangle(point k1, point k2) {
return k1.getP() < k2.getP() || (k1.getP() == k2.getP() && sign(cross(k1, k2)) > 0);
}
point proj(point k1, point k2, point q) { // q 到直線 k1,k2 的投影
point k = k2 - k1; return k1 + k * (dot(q - k1, k) / k.len2());
}
point reflect(point k1, point k2, point q) { return proj(k1, k2, q) * 2 - q; } // q 關於直線 k1,k2 的對稱點
int clockwise(point k1, point k2, point k3) { // k1 k2 k3 逆時針1 順時針-1 否則0
return sign(cross(k2 - k1, k3 - k1));
}
int checkLL(point k1, point k2, point k3, point k4) { // 求直線(L) 線段(S) k1,k2 和 k3,k4 的交點
return cmp(cross(k3 - k1, k4 - k1), cross(k3 - k2, k4 - k2)) != 0;
}
struct line {
point p[2];
line() {}
line(point k1, point k2) { p[0] = k1, p[1] = k2; }
point& operator [] (int k) { return p[k]; }
point dir() { return p[1] - p[0]; }
bool include(point k) { return sign(cross(p[1] - p[0], k - p[0])) > 0; }
line push(db len) { // 向外(左手邊)平移 len 個單位
point delta = (p[1] - p[0]).turn90().unit() * len;
return line(p[0] - delta, p[1] - delta);
}
}triangle[3];
bool parallel(line k1, line k2) { return sign(cross(k1.dir(), k2.dir())) == 0; }
bool sameDir(line k1, line k2) { return parallel(k1, k2) && sign(dot(k1.dir(), k2.dir())) == 1; }
bool operator < (line k1, line k2) {
if (sameDir(k1, k2)) return k2.include(k1[0]);
return compareangle(k1.dir(), k2.dir());
}
point getLL(point k1, point k2, point k3, point k4) { // 兩直線交點
db w1 = cross(k1 - k3, k4 - k3), w2 = cross(k4 - k3, k2 - k3);
return (k1 * w2 + k2 * w1) / (w1 + w2);
}
point getLL(line k1, line k2) { return getLL(k1[0], k1[1], k2[0], k2[1]); }
bool checkpos(line k1, line k2, line k3) { return k3.include(getLL(k1, k2)); }
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
int t; cin >> t;
while (t--) {
db len, x, y, vx, vy = 0.0;
db L = 0.0, R = 1e20, mid;
long long k;
vector<db> initTime(3); // 第一次交直線的時間
vector<db> crossTime(3); // 兩次相交的間隔時間
vector<bool> vis(3, false);
cin >> len >> x >> y >> vx >> vy >> k;
triangleVertex[0] = point(0.5 * len, 0);
triangleVertex[1] = point(-0.5 * len, 0);
triangleVertex[2] = point(0, 0.5 * sqrt(3.0) * len);
triangle[0] = line(triangleVertex[0], triangleVertex[1]);
triangle[1] = line(triangleVertex[1], triangleVertex[2]);
triangle[2] = line(triangleVertex[2], triangleVertex[0]);
point now = point(x, y);
point dir = point(vx, vy) + now;
line vec = line(now, dir);
for (int i = 0; i < 3; ++i) {
if (parallel(vec, triangle[i])) {
vis[i] = true;
continue;
}
point intersection = getLL(vec, triangle[i]);
if (!sameDir(line(now, intersection), vec)) {
triangle[i] = triangle[i].push(0.5 * sqrt(3.0) * len);
intersection = getLL(vec, triangle[i]);
}
initTime[i] = now.dis(intersection);
line nxtL = triangle[i].push(0.5 * sqrt(3.0) * len);
point nxt = getLL(vec, nxtL);
crossTime[i] = intersection.dis(nxt);
}
auto cal = [&](int i, db time) {
if (vis[i]) return 0ll;
long long cnt = 0;
if (cmp(time, initTime[i]) == 1) {
time -= initTime[i]; ++cnt;
cnt += (long long)floor(time / crossTime[i]);
}
return cnt;
};
while (R - L > eps) {
mid = 0.5 * (L + R);
long long num = 0;
for (int i = 0; i < 3; ++i) num += cal(i, mid);
if (num >= k) R = mid;
else L = mid;
}
cout << fixed << setprecision(10) << mid / sqrt(vx * vx + vy * vy) << '\n';
}
return 0;
}
1009 Parentheses Matching
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
char s[maxn];
int main() {
int t;
scanf("%d", &t);
while (t--) {
scanf("%s", s + 1);
int n = strlen(s + 1);
stack<int>s1;
queue<int>s2;
for (int i = n; i > 0; i--) {
if (s[i] == ')') s1.push(i);
else if (s[i] == '(') {
if (!s1.empty()) s1.pop();
else s2.push(i);
}
}
for (int i = 1; i <= n; i++) {
if (s1.empty()) break;
if (s[i] == '*') {
int x = s1.top();
if (i < x) {
s[i] = '(';
s1.pop();
}
}
}
for (int i = n; i >= 1; i--) {
if (s2.empty()) break;
if (s[i] == '*') {
int x = s2.front();
if (i > x) {
s[i] = ')';
s2.pop();
}
}
}
if ((!s1.empty()) || (!s2.empty())) {
printf("No solution!\n");
}
else {
for (int i = 1; i <= n; i++) {
if (s[i] != '*') printf("%c", s[i]);
}
printf("\n");
}
}
return 0;
}