cbh大爷说:写博客不能弃坑。
orz cbh
那我就来更新博客了。
violet这个系列的题好神啊……出题人好劲啊……
……怎么最近都在理性愉悦啊……
另外bzoj400题纪念~
2708: [Violet 1]木偶
首先先对所有木偶按照特征值排序
考虑一种木偶与绳索配对的方法:
木偶(1)与绳索(k+1)配对,木偶(2)与绳索(k+2)配对……木偶(n-k)与绳索(n)配对。
当木偶(n-k+1)无法与绳索(k)配对时,这样的配对方法能扔掉(k)个木偶。
容易证明,最优的配对方式一定可以分成许多段,每段内用这种方式进行配对。
于是可以dp。
首先令(g_{l,r})表示l~r之间用那种方式配对最多能扔掉多少个木偶,(g_{l,r})可以通过枚举(k)算出,时间复杂度(O(n^3))
令(f_i)表示到1~i之间能扔掉多少个木偶,那么有(f_i=maxleft { f_j+g_{j+1,i} ight })。直接dp,时间复杂度(O(n^2))
时间复杂度为(O(n^2))
#include <bits/stdc++.h> #define N 100 using namespace std; int n; int f[N], ai[N]; int g[N][N]; int main() { while (scanf("%d", &n) !=EOF) { for (int i = 1; i <= n; ++ i) scanf("%d", &ai[i]); sort(ai + 1, ai + n + 1); for (int i = 1; i <= n; ++ i) for (int j = i; j <= n; ++ j) { int bo = 1; for (int k = 1; k <= j - i + 1; ++ k) { int p, q; for (p = i, q = i + k; q <= j; ++ p, ++ q) if (ai[q] - ai[p] > 1) bo = 0; if (abs(ai[p] - ai[i + k - 1]) <= 1) bo = 0; if (!bo) {g[i][j] = k - 1; break;} } if (bo) g[i][j] = j - i + 1; //if (g[i][j] != cal(i, j)) // puts("haha"); } for (int i = 1; i <= n; ++ i) f[i] = 0; for (int i = 1; i <= n; ++ i) for (int j = 0; j < i; ++ j) f[i] = max(f[i], f[j] + g[j + 1][i]); printf("%d ", f[n]); } }
2709: [Violet 1]迷宫花园
二分答案,判断最短路,期间因为eps的问题wa了许多发QAQ
时间复杂度(O(RClogRClogv))
#include <bits/stdc++.h> #define INF 1000000000 using namespace std; int r, c; double ll; char mp[110][110]; int get(int rr, int cc) { return (rr - 1) * c + cc; } int dr[4] = {1, -1, 0, 0}, dc[4] = {0, 0, 1, -1}; vector <int> bi[10010]; vector <double> ci[10010]; int s, e; void build(int a, int b, double c) { bi[a].push_back(b); ci[a].push_back(c); bi[b].push_back(a); ci[b].push_back(c); } struct node { int t; double l; }; bool operator < (node a, node b) {return a.l > b.l;} priority_queue <node> H; int vis[10010]; double dis[10010]; int main() { int T; cin >> T; while (T --) { cin >> ll >> r >> c; for (int i = 1; i <= r; i++) { char cc; scanf("%c", &cc); while (cc != ' ') scanf("%c", &cc); for (int j = 1; j <= c; j++) { scanf("%c", &cc); while (cc == ' ') scanf("%c",&cc); mp[i][j] = cc; if (cc=='S') s = get(i, j); if (cc=='E') e = get(i, j); } } { double lb = 0, rb = 10; while (rb - lb > 0.0000001) { double md = (lb + rb) / 2; for (int i = 1; i <= r * c; ++ i) bi[i].clear(), ci[i].clear(); for (int i = 1; i <= r; ++ i) for (int j = 1; j <= c; ++ j) for (int d = 0; d < 4; ++ d) if (mp[i][j] != '#' && mp[i + dr[d]][j + dc[d]] != '#') build(get(i, j), get(i + dr[d], j + dc[d]), (d >= 2? 1: md)); for (int i = 1; i <= r * c; ++ i) dis[i] = INF, vis[i] = 0; while (!H.empty()) H.pop(); dis[s] = 0, H.push((node){s, 0}); while (!H.empty()) { int hd; do hd = H.top().t, H.pop(); while (vis[hd] && !H.empty()); if (!vis[hd]) vis[hd] = 1; else break; for (int i = 0; i < bi[hd].size(); ++ i) if (dis[hd] + ci[hd][i] < dis[bi[hd][i]]) { dis[bi[hd][i]] = dis[hd] + ci[hd][i]; H.push((node){bi[hd][i], dis[bi[hd][i]]}); } } if (dis[e] > ll) rb = md; else lb = md; } printf("%.5lf ", lb); } } }
2710: [Violet 1]追风者
好神的题啊,我不会做QAQ
2711: [Violet 2]After 17
题目要求最小化(sum_{i=1}^{n}sum_{j=i+1}^{n}x_i x_j + y_i y_j),于是(x)和(y)可以分别考虑。
(sum_{i=1}^{n}sum_{j=i+1}^{n}x_i x_j = frac{(sum_{i=1}^{n}x_i)^2-sum_{i=1}^{n}x_i^2}{2})
通过归纳法(?)可以证明当(sum abs(x_i))最大时最优。因此(sum_{i=1}^{n}x_i)^2)是定值,于是就可以用dp去求(sum_{i=1}^{n}x_i^2)的最大值。
时间复杂度(O(nl))
#include <bits/stdc++.h> using namespace std; #define N 210 #define M 40010 int f[N][M * 2]; int n; int xi[N], yi[N]; double ans; void solve(int d[]) { for (int i = 0; i <= n; ++ i) for (int j = -M; j < M; ++ j) f[i][j + M] = 0; f[0][M] = 1; for (int i = 0; i < n; ++ i) for (int j = -M; j < M; ++ j) if (f[i][j + M]) f[i + 1][j + d[i + 1] + M] = f[i + 1][j - d[i + 1] + M] = 1; for (int i = 0; i < M; ++ i) if (f[n][M + i] || f[n][M - i]) { ans += pow(i, 2); break; } } int main() { scanf("%d", &n); for (int i = 1; i <= n; ++ i) { scanf("%d%d", &xi[i], &yi[i]); ans -= pow(xi[i], 2) + pow(yi[i], 2); } solve(xi); solve(yi); printf("%.2lf", ans / 2); }
2712: [Violet 2]棒球
第二道神题啊QAQ
考虑将边界化成分数,为什么要化成分数呢?因为精度问题。
化成分数后就转成了这么个问题:求分数(frac{p}{q}),最小化(p),(q),使得(frac{lp}{lq} < frac{p}{q} leq frac{rp}{rq})
若(frac{p}{q} = frac{rp}{rq}),那么(q=frac{rq}{gcd(rp, rq)})。
剩下得就是(frac{lp}{lq} < frac{p}{q} < frac{rp}{rq})
考虑如下做法
若有一个整数(p)满足(frac{lp}{lq} < p < frac{rp}{rq}),那么就输出(p)
如果(lp<lq)且(rp<rq),则去递归求(frac{rq}{rp} < frac{p'}{q'} < frac{lq}{lp}),那么原问题得解就是(frac{q'}{p'})
否则,令(k=left lfloor frac{lp}{lq} ight floor),递归求(frac{rq - k * rp}{rp} < frac{p'}{q'} < frac{lq - k * lp}{lp}),那么原问题得解就是(frac{p' + k * q'}{q'})
算法的正确性是显然的。递归时新问题的最优解(frac{p'}{q'})一定对应原问题的最优解(frac{p}{q})。如果前者对应的不是后者的最优解,那么就一定可以通过原问题的最优解构造出一个新问题的解(frac{p''}{q''}),使得这个解比(frac{p'}{q'})更优。
注意边界问题
时间复杂度(O(r))
#include <bits/stdc++.h> #define LL long long #define pL pair <LL, LL> using namespace std; int n; LL r; pL solve(LL lu, LL ld, LL ru, LL rd) { LL x = lu / ld + 1; if (x * rd < ru) return pL(x, 1); if (!lu) return pL(1, rd / ru + 1); if (lu <= ld && ru <= rd) {pL nw = solve(rd, ru, ld, lu); return pL(nw.second, nw.first);} x = lu / ld; pL nw = solve(lu - ld * x, ld, ru - rd * x, rd); nw.first += nw.second * x; return nw; } int main() { while (scanf("%d 0.%lld", &n, &r) == 2) { if (r == 0) {puts("1"); continue;} LL x = 10; while (n --) x *= 10; LL lu = r * 10 - 5, ld = x, ru = r * 10 + 5, rd = x; for( ; lu % 5 == 0 && ld % 5 == 0; lu /= 5, ld /= 5); for( ; ru % 5 == 0 && rd % 5 == 0; ru /= 5, rd /= 5); pL ans = solve(lu, ld, ru, rd); //printf("%lld %lld ", ans.first, ans.second); printf("%lld ", min(ans.second, ld)); } }
2713: [Violet 2]愚蠢的副官
第三道神题……我dp被卡常数了,极限数据要跑8s……交标程走人
2714: [Violet 3]交替和
数位dp
定义一个数的交替和为将该数从高位到低位添加至序列L后,A(L)的值,比如(1101B)的交替和为(-1)
定义(f_{0,i})表示b进制下,所有可以有前导零的i位数的交替和的和。
定义(f_{1,i})表示b进制下,所有可以有前导零的i位偶数的交替和减去所有可以有前导零的i位奇数的交替和
设(n)在(b)进制下有(len)位
那么答案由两部分组成。第一部分是位数在(1)~(len-1)之间的数,第二部分是位数为(len)的数。
维护当前要计算的第一个数位对答案的贡献的系数(np)。
第一部分枚举位数,最高位,用(f)以及(np)计算贡献
第二部分枚举与n从第几位开始不同,以及这一位选了多少,依然用(f)以及(np)计算贡献
时间复杂度(O(blogn))
/************************************************************** Problem: 2714 User: AwD Language: C++ Result: Accepted Time:0 ms Memory:1296 kb ****************************************************************/ #include <bits/stdc++.h> #define LL long long using namespace std; LL b, n; LL f[2][100], g[100]; LL ni[100], nl, ans; int main() { cin >> b >> n; g[0] = 1; for (LL i = 1, j = 1; j <= n; ++ i, j *= b) { LL np = 1; for (LL k = 0; k < b; ++ k) f[0][i] += f[0][i - 1] * -1 + g[i - 1] * k, f[1][i] += f[1][i - 1] * -np + (g[i - 1] & 1) * k * np, np = np * ((g[i - 1] * i)& 1? -1: 1); g[i] = g[i - 1] * b; } while (n) { ni[++ nl] = n % b; n /= b; } { LL np = 1, s = 0; for (LL i = 1; i < nl; ++ i) { for (LL k = 1; k < b; ++ k) ans += f[i & 1][i - 1] * -np + (i & 1? g[i - 1] & 1: g[i - 1]) * k * np, np = np * ((g[i - 1] * i) & 1? -1: 1); } //cout << ans << " "; for (LL i = nl; i >= 1; -- i) { for (LL k = (i == nl); k <= (ni[i] - (i != 1)); ++ k) ans += f[nl & 1][i - 1] * ((nl - i) & 1? 1: -1) * np + (nl & 1? g[i - 1] & 1: g[i - 1]) * (s + ((nl - i) & 1? -1: 1) * k) * np, np = np * ((g[i - 1] * nl) & 1? -1: 1); s += ((nl - i) & 1? -1: 1) * ni[i]; } cout << ans << " "; } }
2715: [Violet 3]最优指令
状压所有状态是否可以出现,简单bfs即可
时间复杂度(O(2^SC))
#include <bits/stdc++.h> #define N (1 << 17) using namespace std; int s, c; int dis[N], vis[N]; queue <int> Q; int pres[N], prei[N]; int trans[18][18]; void dfs(int t) { if (t != (1 << s) - 1) { dfs(pres[t]); if (prei[t] < 10) printf("%d", prei[t]); else printf("%c", prei[t] - 10 + 'a'); } } int main() { scanf("%d%d", &s, &c); for (int i = 0; i < s; ++ i) for (int j = 0; j < c; ++ j) scanf("%d", &trans[i][j]); Q.push((1 << s) - 1); vis[(1 << s) - 1] = 1; while (!Q.empty()) { int hd = Q.front(); Q.pop(); for (int i = 0; i < c; ++ i) { int nw = 0; for (int j = 0; j < s; ++ j) if (hd & (1 << j)) nw |= (1 << trans[j][i]); if (!vis[nw]) { vis[nw] = 1; dis[nw] = dis[hd] + 1; pres[nw] = hd; prei[nw] = i; Q.push(nw); } } } if (!vis[1]) puts("impossible"); else dfs(1); }
2716: [Violet 3]天使玩偶
裸的k-d树,我的常数写的好挫啊QAQ
时间复杂度(O((n+m)sqrt{n + m}))
#include <bits/stdc++.h> #define INF 100000000 #define N 1000100 using namespace std; int n, m; struct point {int d[2];}; struct node { int opt, ch[2], si; int bd[2][2]; point mid; } tr[N]; stack <int> S; int nw, tmp; int comp(point a, point b) {return a.d[nw] < b.d[nw];} point ls[N]; void dfs_dele(int t) { if (tr[t].ch[0]) dfs_dele(tr[t].ch[0]); ls[++ tmp] = tr[t].mid; if (tr[t].ch[1]) dfs_dele(tr[t].ch[1]); tr[t].ch[0] = tr[t].ch[1] = 0; tr[t].opt = tr[t].si = 0; S.push(t); } void update(int t) { tr[t].bd[0][0] = min(tr[t].mid.d[0], min(tr[tr[t].ch[0]].bd[0][0], tr[tr[t].ch[1]].bd[0][0])); tr[t].bd[0][1] = max(tr[t].mid.d[0], max(tr[tr[t].ch[0]].bd[0][1], tr[tr[t].ch[1]].bd[0][1])); tr[t].bd[1][0] = min(tr[t].mid.d[1], min(tr[tr[t].ch[0]].bd[1][0], tr[tr[t].ch[1]].bd[1][0])); tr[t].bd[1][1] = max(tr[t].mid.d[1], max(tr[tr[t].ch[0]].bd[1][1], tr[tr[t].ch[1]].bd[1][1])); } int rebuild(int opt, int l, int r) { nw = opt; sort(ls + l, ls + r + 1, comp); int mid = (l + r) / 2; int hd = S.top(); S.pop(); tr[hd].mid = ls[mid]; tr[hd].opt = opt; tr[hd].si = r - l + 1; if (l < mid) tr[hd].ch[0] = rebuild(!opt, l, mid - 1); if (mid < r) tr[hd].ch[1] = rebuild(!opt, mid + 1, r); update(hd); return hd; } int insert(int opt, int t, point nw) { if (!t) { t = S.top(); S.pop(); tr[t].opt = opt; tr[t].si = 1; tr[t].mid = nw; } else { int k = tr[t].mid.d[tr[t].opt] < nw.d[tr[t].opt]; tr[t].si ++; tr[t].ch[k] = insert(!opt, tr[t].ch[k], nw); if (1.0 * tr[tr[t].ch[k]].si / tr[t].si > 0.7) { tmp = 0; dfs_dele(t); t = rebuild(opt, 1, tmp); } } update(t); return t; } int nowans; int dis(point a, point b) { return abs(a.d[0] - b.d[0]) + abs(a.d[1] - b.d[1]); } int dis(point a, int t) { int tmp = 0; for (int i = 0; i <= 1; ++ i) if (a.d[i] < tr[t].bd[i][0]) tmp += tr[t].bd[i][0] - a.d[i]; for (int i = 0; i <= 1; ++ i) if (a.d[i] > tr[t].bd[i][1]) tmp += a.d[i] - tr[t].bd[i][1]; return tmp; } int s, dp; void ask(int t, point nw) { s ++; if (dis(tr[t].mid, nw) < nowans) nowans = dis(tr[t].mid, nw); int b[2]; b[0] = dis(nw, tr[t].ch[0]); b[1] = dis(nw, tr[t].ch[1]); int k = b[1] < b[0]; if (tr[t].ch[k]) if (b[k] < nowans) ask(tr[t].ch[k], nw); if (tr[t].ch[!k]) if (b[!k] < nowans) ask(tr[t].ch[!k], nw); //dp --; } int root; int main() { tr[0].bd[0][0] = tr[0].bd[1][0] = INF; scanf("%d%d", &n, &m); for (int i = N - 1; i >= 1; -- i) S.push(i); for (int i = 1; i <= n + m; ++ i) { int t = 1, x, y; if (i > n) scanf("%d", &t); scanf("%d%d", &x, &y); nowans = INF; if (t == 1) root = insert(0, root, (point){x, y}); else { s = 0; ask(root, (point){x, y}); printf("%d ", nowans); } } }
2717: [Violet 4]迷路的兔子
第四道神题= =
奥妙重重的构造题,只要知道这是对的就好了是不是哇QAQ
时间复杂度(O(n^2))
#include <bits/stdc++.h> #define N 1000 using namespace std; int n, x; int remain[N][N]; int main() { cin >> n; cout << n * (n - 1) / 2 << " "; for (int i = 1; i <= (n >> 1); ++ i) for (int j = 1; j <= n; ++ j) cout << j << " " << (j + i - 1) % n + 1 << " " << (j + i + i - 1) % n + 1 << " "; }
2718: [Violet 4]毕业旅行
建二分图,把每个点(i)拆成两个点(A_i),(B_i)
用传递闭包把原图中每个点能到达的点都跑出来,若(i)能到达(j),那么就在新图中将(A_i)与(B_j)连一条边。
那么原图的独立集就相当于新图中的独立集。直接上匈牙利。
时间复杂度(O(n^3))
#include <bits/stdc++.h> using namespace std; int n, m; int sx[210][210]; int vx[210], vy[210], pr[210]; int ans; int dfs(int t) { vx[t] = 1; for (int i = 1; i <= n; ++ i) if (sx[t][i] && !vy[i] && !vx[pr[i]]) { if (!pr[i] || dfs(pr[i])) { pr[i] = t; return 1; } } return 0; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= m; ++ i) { int a, b; scanf("%d%d", &a, &b); sx[a][b] = 1; } for (int k = 1; k <= n; ++ k) for (int i = 1; i <= n; ++ i) for (int j = 1; j <= n; ++ j) if (sx[i][k] && sx[k][j]) sx[i][j] |= 1; for (int i = 1; i <= n; ++ i) { for (int j = 1; j <= n; ++ j) vx[j] = vy[j] = 0; if (dfs(i)) ans ++; } printf("%d", n - ans); }
2719: [Violet 4]银河之星
由于第二种移动的存在,所有在模三意义下同余的位置是等价的,因此可以缩在一起。
直接上记搜。
注意两个坑点:
1.由于棋盘大小的限制,某些情况下第二种移动是没法做的,因此要现判断这些移动的可行性。
2.由于棋盘大小的限制,每种位置能放的棋子的数量是有上限的,搜索时要判断一下
时间复杂度O(跑的出)
#include <bits/stdc++.h> using namespace std; int k, n, m, fin_x, fin_y; int fin; struct status { int ss[9]; status () {for (int i = 0; i < 9; ++ i) ss[i] = 0;} }; int operator <(status a, status b) { for (int i = 0; i < 9; ++ i) if (a.ss[i] != b.ss[i]) return a.ss[i] < b.ss[i]; return 0; } map <status, int> M; int xd[8] = {-1, -1, -1, 0, 1, 1, 1, 0}; int yd[8] = {-1, 0, 1, 1, 1, 0, -1, -1}; int ai[1000], bi[1000], ci[1000], tot; int cg[9][9], mx[9]; int get_pos(int x, int y) { x = x % 3; y = y % 3; return x * 3 + y; } int dfs(status nw) { if (M.count(nw)) return M[nw]; int is_ans = 1; for (int i = 0; i < 9; ++ i) if (((bool)nw.ss[i]) ^ (i == fin)) is_ans = 0; if (is_ans) return 1; for (int i = 0; i < tot; ++ i) if (nw.ss[ai[i]] && nw.ss[bi[i]]) { status nx = nw; nx.ss[ai[i]] --; nx.ss[bi[i]] --; nx.ss[ci[i]] ++; if (nx.ss[ci[i]] > mx[ci[i]]) continue; if (dfs(nx)) return M[nw] = 1; } return M[nw] = 0; } void init() { for (int i = 0; i < 9; ++ i) for (int j = 0; j < 9; ++ j) cg[i][j] = -1; for (int i = 0; i < 9; ++ i) mx[i] = 0; for (int i = 1; i <= n; ++ i) for (int j = 1; j <= m; ++ j) { mx[get_pos(i, j)] ++; for (int d = 0; d < 8; ++ d) if (i + xd[d] * 2 >= 1 && i + xd[d] * 2 <= n && j + yd[d] * 2 >= 1 && j + yd[d] * 2 <= m) cg[get_pos(i, j)][get_pos(i + xd[d], j + yd[d])] = get_pos(i + xd[d] * 2, j + yd[d] * 2); } tot = 0; for (int i = 0; i < 9; ++ i) for (int j = 0; j < 9; ++ j) if (cg[i][j] >= 0) { ai[tot] = i; bi[tot] = j; ci[tot] = cg[i][j]; tot ++; } } int main() { while (scanf("%d%d%d%d%d", &k, &n, &m, &fin_x, &fin_y) == 5) { fin = get_pos(fin_x, fin_y); M.clear(); status start; for (int i = 1; i <= k; ++ i) { int x, y; scanf("%d%d", &x, &y); start.ss[get_pos(x, y)] ++; } init(); if (dfs(start)) puts("Yes"); else puts("No"); } }
2720: [Violet 5]列队春游
数学题,推推式子可以得到结论
时间复杂度(O(nlogn))
#include <bits/stdc++.h> using namespace std; int n, s; int ai[400]; double ans; int main() { scanf("%d", &n); for (int i = 1; i <= n; ++ i) scanf("%d", &ai[i]); sort(ai + 1, ai + n + 1); for (int i = 1, j = 0; i <= n; ++ i) if (ai[i] != ai[i + 1]) { ans += 1.0 * (i - j) * (n + 1) / (n + 1 - j); j = i; } printf("%.2lf", ans); }
2721: [Violet 5]樱花
又是一道数学题,答案是((n!)^2)的约数个数,筛一下素数即可
时间复杂度(O(n)) (还是(O(nloglogn))?)
#include<cstdio> #include<cmath> #include<ctime> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #define ll long long #define mod 1000000007 #define inf 1000000000 using namespace std; int read() { int x=0;char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x; } int n,cnt; ll ans=1; int pri[1000005],mn[1000005],num[1000005]; bool del[1000005]; void getpri() { for(int i=2;i<=n;i++) { if(!del[i])pri[++cnt]=i,mn[i]=cnt; for(int j=1;pri[j]*i<=n&&j<=cnt;j++) { del[pri[j]*i]=1;mn[pri[j]*i]=j; if(i%pri[j]==0)break; } } } void cal(int x) { while(x!=1) { num[mn[x]]++; x/=pri[mn[x]]; } } int main() { n=read(); getpri(); for(int i=1;i<=n;i++) cal(i); for(int i=1;i<=cnt;i++) ans=ans*(num[i]*2+1)%mod; printf("%lld ",ans); return 0; }
2722: [Violet 5]爱的花环
还是神题= =
由于(A_{i,j}=A_{j,i})
因此(l_{i,j})和(l_{j,i})的限制,(r_{i,j})和(r_{j,i})的限制可以分别合并在一起。
且(sum_{i=1}^{n}sum_{j=1}^{n}A_{i,j}=0)可以化为:(sum_{i=1}^{n}A_{i,i} + 2sum_{i=1}^{n}sum_{j=i+1}^{n}A_{i,j}=0)。
考虑做以下变换:
对于所有满足(i!=j)的数对((i,j)),将(l_{i,j})、(r_{i,j})翻倍。
对于所有(k_{i,i}),将其翻倍。
在这个变换之后,原问题就转化为了:求一个满足
(sum_{i=1}^{n}sum_{j=i}^{n}A_{i,j}=0),
(l_{i,j}leq A_{i,j}leq r_{i,j}),
对于所有(i != j),有(A_{i,j}mod2=0)
这些限制的数组(A),使得(sum_{i=1}^{n}sum_{j=i}^{n}k_{i,j}A_{i,j})最大。
这个问题得到的答案是原问题的两倍。
现考虑如何解决这个问题。
首先先排除对于所有(i != j),有(A_{i,j}mod2=0)这个限制,由于(k_{i,j})为正,可以这样做:
1.将所有(A_{i,j})赋值为(r_{i,j})
2.如果当前(sum_{i=1}^{n}sum_{j=i}^{n}A_{i,j}=0),结束,返回答案
3.否则在满足(A_{i,j}!=l_{i,j})的数对((i,j))中找一个(k_{i,j})最小的,将它的(A_{i,j})减小,减小到满足下面任意一条限制为止:
a.(sum_{i=1}^{n}sum_{j=i}^{n}A_{i,j}=0)
b.(A_{i,j}=l_{i,j})
4.转至步骤2
这个贪心的正确性是明显的。
那么加上对于所有(i != j),有(A_{i,j}mod2=0) 这个限制。考虑会发生什么:
可能会出现一个(A_{i,j}(i != j))变成奇数了。
为了满足这个限制,需要改变它的奇偶性。这个值从哪里来?只能从某一个(A_{p,p})中来。
让答案尽可能的优,我们需要找到一个(abs(k_{p,p} - k_{i,j}))最小的((p, p)),并在((p, p)),((i, j))之间移动一个1即可。
然后就是这样了……
时间复杂度(O(n^2logn^2))
#include <bits/stdc++.h> #define N 300000 #define INF 100000000 #define LL long long using namespace std; LL n; LL ss[600][600], pp[600][600], tot; LL ki[N], li[N], ri[N]; LL nw[N]; LL ord[N], nwsum, ans; LL comp(LL a, LL b) { return ki[a] < ki[b]; } int main() { //freopen("garland6.in", "r", stdin); scanf("%lld", &n); for (LL i = 1; i <= n; ++ i) ss[i][i] = ++ tot; for (LL i = 1; i <= n; ++ i) for (LL j = i + 1; j <= n; ++ j) ss[i][j] = ss[j][i] = ++ tot; for (LL i = 1; i <= n * (n - 1); ++ i) li[i] = -INF, ri[i] = INF, ki[i] = 0; for (LL i = 1; i <= n; ++ i) for (LL j = 1; j <= n; ++ j) { LL a; scanf("%lld", &a); ki[ss[i][j]] += a; } for (LL i = 1; i <= n; ++ i) for (LL j = 1; j <= n; ++ j) { LL a; scanf("%lld", &a); li[ss[i][j]] = max(li[ss[i][j]], a); } for (LL i = 1; i <= n; ++ i) for (LL j = 1; j <= n; ++ j) { LL a; scanf("%lld", &a); ri[ss[i][j]] = min(ri[ss[i][j]], a); } for (LL i = 1; i <= n; ++ i) ki[i] *= 2; for (LL i = n + 1; i <= tot; ++ i) li[i] *= 2, ri[i] *= 2; //for (LL i = 1; i <= tot; ++ i) cout << li[i] << " "; cout << " "; //for (LL i = 1; i <= tot; ++ i) cout << ri[i] << " "; cout << " "; //for (LL i = 1; i <= tot; ++ i) cout << ki[i] << " "; cout << " "; for (LL i = 1; i <= tot; ++ i) ord[i] = i; sort(ord + 1, ord + tot + 1, comp); for (LL i = 1; i <= tot; ++ i) nw[i] = ri[i], nwsum += ri[i]; for (LL i = 1; i <= tot; ++ i) if (nwsum > ri[ord[i]] - li[ord[i]]) nwsum -= ri[ord[i]] - li[ord[i]], nw[ord[i]] = li[ord[i]]; else if (ord[i] <= n || nwsum % 2 == 0) { nw[ord[i]] -= nwsum; break; } else { nw[ord[i]] -= nwsum; LL p = i, q = i; while (ord[q] > n && q <= tot) ++ q; while (ord[p] > n && p >= 1) -- p; if (p < 1 || (q <= tot && ki[ord[q]] - ki[ord[i]] < ki[ord[i]] - ki[ord[p]])) { nw[ord[i]] ++; nw[ord[q]] --; } else { nw[ord[i]] --; nw[ord[p]] ++; } break; } for (LL i = 1; i <= n; ++ i) for (LL j = 1; j <= n; ++ j) if (i == j) pp[i][j] = nw[ss[i][j]]; else { pp[i][j] = nw[ss[i][j]] / 2; if (nw[ss[i][j]] % 2 == 1) puts("mdzz"); } /* for (LL i = 1; i <= n; ++ i) { for (LL j = 1; j <= n; ++ j) printf("%d ", pp[i][j]); puts(""); }*/ for (LL i = 1; i <= tot; ++ i) ans += nw[i] * ki[i]; printf("%lld", ans / 2); }
2723: [Violet 6]星之波动
好像很神的样子QAQ,不敢看
2724: [Violet 6]蒲公英
很显然的分块,l~r之间的众数只可能出现在:l~块a的众数,块a~块b的众数,块b~r的众数之间,预处理出任意两块之间的众数,乱搞即可。
时间复杂度(O(nsqrt{n}))
#include <bits/stdc++.h> #define N 50000 #define P 1000 using namespace std; int n, m, s; int ai[N], si[N]; int mx[P][P], in[N], st[N]; vector <int> L[N], S; int nowi[N], nowmax; int get(int t, int l, int r) { int p = (upper_bound(L[t].begin(), L[t].end(), r) - lower_bound(L[t].begin(), L[t].end(), l)) * (n + 1) - t; return p; } int main() { scanf("%d%d", &n, &m); s = sqrt(n / (log(n) / log(2))) + 1; for (int i = 1; i <= n; ++ i) scanf("%d", &ai[i]), si[i] = ai[i]; sort(si + 1, si + n + 1); for (int i = 1; i <= n; ++ i) ai[i] = lower_bound(si + 1, si + n + 1, ai[i]) - si; for (int i = 1; i <= n; ++ i) L[ai[i]].push_back(i); for (int i = 1, p = 1; i <= n; i += s, ++ p) for (int j = 1; j <= s && i + j - 1 <= n; ++ j) in[i + j - 1] = p; for (int i = 1; i <= n; i += s) { for (int j = 1; j <= n; ++ j) nowi[j] = 0; for (int j = i; j <= n; ++ j) { nowi[ai[j]] ++; if (nowi[ai[j]] > nowi[nowmax] || nowi[ai[j]] == nowi[nowmax] && ai[j] < nowmax) nowmax = ai[j]; if (in[j] != in[j + 1]) mx[in[i]][in[j]] = nowmax; } } for (int i = 1, last = 0; i <= m; ++ i) { int l, r, ans = 0; scanf("%d%d", &l, &r); //last = 0; l = (l + last - 1) % n + 1; r = (r + last - 1) % n + 1; if (l > r) swap(l, r); S.clear(); if (in[l] == in[r]) for (int i = l; i <= r; ++ i) S.push_back(ai[i]); else { for (int i = l; in[i] == in[l]; ++ i) S.push_back(ai[i]); for (int i = r; in[i] == in[r]; -- i) S.push_back(ai[i]); S.push_back(mx[in[l] + 1][in[r] - 1]); } for (int i = 0; i < S.size(); ++ i) if (get(S[i], l, r) > get(ans, l, r)) ans = S[i]; ans = si[ans]; printf("%d ", ans); last = ans; } }
2725: [Violet 6]故乡的梦
终于要写完了QAQ
首先从(s)开始建最短路径生成树。
定义关键路径为该树上(s)->(t)的路径。
定义一个点的(dep)
若一个点在关键路径上,则该点的(dep)为它是关键路径上的第几个点。
若一个点不在关键路径上,则该点的(dep)为它沿着树边走,到达第一个在关键路径上的点的(dep)。
很明显,如果切断的边((x, y))如果不在关键路径上的话,最短路是不会变的。
因此考虑((x, y))在树上的情况,假定x是y的父亲。
考虑这时的路径是怎样的:(s)->(x')->(y')->(t)
其中(s)->(x')是在原图上(s)到(x')的最短路,且(x')尽可能的晚,(y')->(t)是原图上(y')到(t)的最短路,且(y')尽可能的早。
这三段一定不能含有(x)->(y)
首先考虑(s)->(x')这一段,要使得其中没有(x)->(y),只需要保证(dep_{x'}leq dep_{x})即可
接着考虑(y')->(t)这一段,要使得其中没有(x)->(y),只需要保证(dep_{y}leq dep_{y'})即可。由于最短路不会绕一圈,这个的正确性是显然的。
最后考虑(x')->(y')这一段
假设(x')->(y')不止一条边,那么设其中有一个点(a),因此这一段就变成了(x')->(a)->(y')
但是由于只切断了一条边,因此(a)到(s)或者(t)中一个点的路径一定是最短路,于之前的设定矛盾。
因此(x')->(y')只有一条边。
于是我们可以枚举每条不在最短路图上的边((a, b)),用它去更新切断边在(left [ dep_{a},dep_{b} ight ])内的答案。用线段树维护即可
时间复杂度(O((n+m)logn))
#include <bits/stdc++.h> #define N 300000 #define M 900000 #define INF 1000000000000000ll #define LL long long using namespace std; LL n, m, s, e; vector <LL> bi[N], ci[N]; LL vis[N]; LL dis[N], die[N], pre[N], lst[N], tot; LL inls[N]; struct node {LL t, d;}; struct comp {LL operator () (node a, node b) {return a.d > b.d;}}; priority_queue <node, vector <node>, comp> H; queue <LL> Q; void dfs1(int t, int p) { inls[t] = p; for (LL i = 0; i < bi[t].size(); ++ i) if (pre[bi[t][i]] == t) if (inls[bi[t][i]]) dfs1(bi[t][i], inls[bi[t][i]]); else dfs1(bi[t][i], p); } LL mn[M], finmn[N]; void put_min(LL t, LL lb, LL rb, LL l, LL r, LL d) { if (lb == l && rb == r) {mn[t] = min(mn[t], d); return;} LL ml = (lb + rb) / 2, mr = (lb + rb) / 2 + 1; if (l <= ml) put_min(t * 2 , lb, ml, l, min(r, ml), d); if (r >= mr) put_min(t * 2 + 1, mr, rb, max(l, mr), r, d); } void dfs_seg(LL t, LL lb, LL rb) { if (lb != rb) { mn[t * 2] = min(mn[t * 2], mn[t]); dfs_seg(t * 2, lb, (lb + rb) / 2); mn[t * 2 + 1] = min(mn[t * 2 + 1], mn[t]); dfs_seg(t * 2 + 1, (lb + rb) / 2 + 1, rb); } else finmn[lb] = mn[t]; } LL q; int main() { scanf("%lld%lld", &n, &m); for (LL i = 1; i <= m; ++ i) { LL a, b, c; scanf("%lld%lld%lld", &a, &b, &c); bi[a].push_back(b); ci[a].push_back(c); bi[b].push_back(a); ci[b].push_back(c); } scanf("%lld%lld", &s, &e); for (LL i = 1; i <= n; ++ i) dis[i] = INF; dis[s] = 0; H.push((node){s, 0}); while (!H.empty()) { LL hd; do hd = H.top().t, H.pop(); while (vis[hd] && !H.empty()); if (vis[hd]) break; else vis[hd] = 1; for (LL i = 0; i < bi[hd].size(); ++ i) if (dis[hd] + ci[hd][i] < dis[bi[hd][i]]) dis[bi[hd][i]] = dis[hd] + ci[hd][i], pre[bi[hd][i]] = hd, H.push((node){bi[hd][i], dis[bi[hd][i]]}); } for (LL i = 1; i <= n; ++ i) vis[i] = 0; for (LL i = 1; i <= n; ++ i) die[i] = INF; die[e] = 0; H.push((node){e, 0}); while (!H.empty()) { LL hd; do hd = H.top().t, H.pop(); while (vis[hd] && !H.empty()); if (vis[hd]) break; else vis[hd] = 1; for (LL i = 0; i < bi[hd].size(); ++ i) if (die[hd] + ci[hd][i] < die[bi[hd][i]]) die[bi[hd][i]] = die[hd] + ci[hd][i], H.push((node){bi[hd][i], die[bi[hd][i]]}); } for (LL p = e; p; p = pre[p]) lst[++ tot] = p, inls[p] = tot; dfs1(s, inls[s]); for (LL i = 1; i <= tot * 4; ++ i) mn[i] = INF; for (LL i = 1; i <= n; ++ i) for (LL j = 0; j < bi[i].size(); ++ j) if (inls[i] > inls[bi[i][j]] && pre[bi[i][j]] != i) put_min(1, 1, tot, inls[bi[i][j]], inls[i] - 1, dis[i] + ci[i][j] + die[bi[i][j]]); dfs_seg(1, 1, tot); scanf("%lld", &q); for (LL i = 1; i <= q; ++ i) { LL a, b; scanf("%lld%lld", &a, &b); if (inls[a] > inls[b]) swap(a, b); if (lst[inls[a]] == a && lst[inls[b]] == b && inls[b] - inls[a] == 1) if (finmn[inls[a]] != INF) printf("%lld ", finmn[inls[a]]); else puts("Infinity"); else printf("%lld ", dis[e]); } }
呼~~~