追逐遊戲 (chase) 50pts
比較卡常;
考慮二分答案,發現我們只需要在知道答案的情況下找出終點即可,所以用倍增找出終點,最後判斷一下合不合法即可;
時間複雜度:$ \Theta(n \log^2 n) $,常數很大,賽時被卡常了;
當然也可以分討做到 $ \Theta(n \log n) $ 複雜度,這裡不細講;
點選檢視程式碼
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n, q;
struct sss{
int t, ne;
}e[500005];
int h[500005], cnt;
inline void add(int u, int v) {
e[++cnt].t = v;
e[cnt].ne = h[u];
h[u] = cnt;
}
#define FI(n) FastIO::read(n)
#define FO(n) FastIO::write(n)
#define Flush FastIO::Fflush()
namespace FastIO {
const int SIZE = 1 << 16;
char buf[SIZE], obuf[SIZE], str[60];
int bi = SIZE, bn = SIZE, opt;
inline int read(register char *s) {
while(bn) {
for (; bi < bn && buf[bi] <= ' '; bi = -~bi);
if (bi < bn) break;
bn = fread(buf, 1, SIZE, stdin);
bi &= 0;
}
register int sn=0;
while(bn) {
for (; bi < bn && buf[bi] > ' '; bi = -~bi) s[sn++] = buf[bi];
if(bi < bn) break;
bn = fread(buf,1,SIZE,stdin);
bi &= 0;
}
s[sn] &= 0;
return sn;
}
inline bool read(register int &x){
int n = read(str), bf = 0;
if(!n) return 0;
register int i=0;
(str[i] == '-') && (bf = 1, i = -~i);
(str[i] == '+') && (i = -~i);
for (x = 0; i < n; i = -~i) x = (x << 3) + (x << 1) + (str[i] ^ 48);
bf && (x = ~x + 1);
return 1;
}
inline bool read(register long long &x) {
int n = read(str), bf = 1;
if(!n) return 0;
register int i=0;
(str[i] == '-') && (bf = -1,i = -~i);
for (x = 0; i < n; i= -~i) x = (x << 3) + (x << 1) + (str[i] ^ 48);
(bf < 0) && (x = ~x + 1);
return 1;
}
inline void write(register int x) {
if(!x) obuf[opt++] = '0';
else {
(x < 0) && (obuf[opt++] = '-', x = ~x + 1);
register int sn = 0;
while(x) str[sn++] = x % 10 + '0', x /= 10;
for (register int i = sn - 1; i >= 0; i = ~-i) obuf[opt++] = str[i];
}
(opt >= (SIZE >> 1)) && (fwrite(obuf, 1, opt, stdout), opt &= 0);
}
inline void write(register long long x) {
if(!x) obuf[opt++] = '0';
else {
(x < 0) && (obuf[opt++] = '-', x = ~x + 1);
register int sn = 0;
while(x) str[sn++] = x % 10 + '0', x /= 10;
for (register int i = sn - 1; i >= 0; i = ~-i) obuf[opt++] = str[i];
}
(opt >= (SIZE >> 1)) && (fwrite(obuf, 1, opt, stdout), opt &= 0);
}
inline void write(register unsigned long long x){
if(!x) obuf[opt++] = '0';
else {
register int sn=0;
while(x) str[sn++] = x % 10 + '0', x /= 10;
for (register int i = sn - 1 ; i >= 0 ; i = ~-i)obuf[opt++] = str[i];
}
(opt >= (SIZE >> 1)) && (fwrite(obuf, 1, opt, stdout), opt &= 0);
}
inline void write(register char x) {
obuf[opt++] = x;
(opt >= (SIZE >> 1)) && (fwrite(obuf, 1, opt, stdout), opt &= 0);
}
inline void Fflush(){
opt && fwrite(obuf, 1, opt, stdout);
opt &= 0;
}
}
int f[200005][25], dep[500005], dfn[500005], dcnt, ff[500005], st[200005][25];
void dfs(int x, int fa) {
dep[x] = dep[fa] + 1;
dfn[x] = ++dcnt;
st[dfn[x]][0] = x;
f[x][0] = fa;
ff[x] = fa;
for (int i = h[x]; i; i = e[i].ne) {
int u = e[i].t;
if (u == fa) continue;
dfs(u, x);
}
}
int p[25];
inline int get(int x,int y){
return dep[x] < dep[y] ? x : y;
}
inline int lca(int x, int y){
if(x == y) return x;
if((x = dfn[x]) > (y = dfn[y])) swap(x, y);
int k = __lg(y - x++);
return ff[get(st[x][k], st[y - (1 << k) + 1][k])];
}
inline bool cck(int s, int t, int x) {
return ((dep[s] + dep[t] - 2 * dep[lca(s, t)]) <= x);
}
inline pair<bool, int> ck(int s, int t, int ss, int lc, int sum, int su, int x) {
int to = 0;
int xx = x;
if (x <= su) {
for (int j = 17; j >= 0; j--) {
if (p[j] > x) continue;
if (dep[f[s][j]] < dep[lc]) continue;
s = f[s][j];
x -= p[j];
}
} else {
x -= su;
s = lc;
}
if (!x) {
to = s;
return {cck(to, ss, xx), to};
} else {
if (sum <= x) {
to = t;
return {cck(to, ss, xx), to};
} else {
sum -= x;
for (int j = 17; j >= 0; j--) {
if (dep[f[t][j]] < dep[lc]) continue;
if (p[j] > sum) continue;
t = f[t][j];
sum -= p[j];
}
to = t;
return {cck(to, ss, xx), to};
}
}
}
int main() {
freopen("chase.in", "r", stdin);
freopen("chase.out", "w", stdout);
FI(n); FI(q);
int x, y;
for (int i = 1; i <= n - 1; i++) {
FI(x); FI(y);
add(x, y);
add(y, x);
}
dfs(1, 0);
p[0] = 1;
for (int j = 1; j <= 17; j++) {
p[j] = p[j - 1] * 2;
for (int i = 1; i + (1 << (j - 1)) <= n; i++) {
st[i][j] = get(st[i + (1 << (j - 1))][j - 1], st[i][j - 1]);
}
for (int i = 1; i <= n; i++) {
f[i][j] = f[f[i][j - 1]][j - 1];
}
}
int z;
for (int i = 1; i <= q; i++) {
FI(x);
FI(y);
FI(z);
int l = 0;
int r = n - 1;
int ans = 0, an = 0;
int sum = 0, su = 0;
int tt = y;
int xx = x;
int lc = lca(x, y);
for (int j = 17; j >= 0; j--) {
if (dep[f[tt][j]] < dep[lc]) continue;
tt = f[tt][j];
sum += p[j];
}
for (int j = 17; j >= 0; j--) {
if (dep[f[xx][j]] < dep[lc]) continue;
xx = f[xx][j];
su += p[j];
}
while(l <= r) {
int mid = (l + r) >> 1;
pair<bool, int> pi = ck(x, y, z, lc, sum, su, mid);
if (pi.first) {
r = mid - 1;
ans = mid;
an = pi.second;
} else l = mid + 1;
}
FO(ans); FO(' '); FO(an); FO('\n');
}
Flush;
return 0;
}
統計 30pts
線段樹暴力30pts;
用Hash亂搞搞就對了。。。
把 $ 1 $ 到 $ m - 1 $ 的Hash值賦成隨機數,$ m $ 的Hash值賦成它們的和的相反數,然後做一遍字首和統計一下相同數的個數然後組合一下即可;
時間複雜度:$ \Theta(Tn \log n) $,瓶頸在排序;
點選檢視程式碼
#include <iostream>
#include <cstdio>
#include <random>
#include <ctime>
#include <algorithm>
using namespace std;
mt19937_64 ran(time(0));
int t;
int n, m;
long long x[1000005];
unsigned long long mp[1000005], sum[1000005], a[1000005];
long long ans;
int main() {
freopen("st.in", "r", stdin);
freopen("st.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> t;
while(t--) {
cin >> n >> m;
ans = 0;
for (int i = 1; i <= n; i++) {
cin >> x[i];
if (!mp[x[i]] && x[i] != m) mp[x[i]] = ran();
}
unsigned long long su = 0;
for (int i = 1; i <= m - 1; i++) {
su += mp[i];
}
mp[m] = -su;
for (int i = 1; i <= n; i++) {
a[i] = mp[x[i]];
}
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i];
sort(sum + 1, sum + 1 + n);
long long summ = 1;
for (int i = 1; i <= n; i++) {
if (sum[i] != sum[i - 1]) {
ans += (summ * (summ - 1)) / 2;
summ = 1;
} else summ++;
}
ans += (summ * (summ - 1)) / 2;
cout << ans << '\n';
for (int i = 1; i <= m; i++) mp[i] = 0;
}
return 0;
}
軟體工程 21pts
直接暴搜21pts;
正解考慮DP,發現完全包含一個區間的區間要麼不選,要麼單獨成一個,所以把這些刪除以後DP;
分兩種情況:有不交的區間,沒有不交的區間;
前者直接輸出前 $ k - 1 $ 大,後者直接DP,設 $ f_{i, j} $ 表示前 $ i $ 個線段分成 $ j $ 段的最大權值,因為只會選連續的一段,所以直接從 $ f_{k, j - 1} $ 轉移而來即可;
最後字首 $ \max $ 最佳化一下,時間複雜度:$ \Theta(nk) $;
點選檢視程式碼
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int n, k;
struct sss{
long long l, r;
bool operator <(const sss &A) const {
if (l == A.l) return r > A.r;
else return l < A.l;
}
}e[50005], c[50005];
bool cmp(sss x, sss y) {
return (x.r - x.l) > (y.r - y.l);
}
vector<long long> v;
long long sum[50005];
int cnt;
long long f[5005][5005];
namespace SEG{
inline int ls(int x) {
return x << 1;
}
inline int rs(int x) {
return x << 1 | 1;
}
struct sss{
int l, r, mi;
}tr[8000005];
inline void push_up(int id) {
tr[id].mi = min(tr[ls(id)].mi, tr[rs(id)].mi);
}
void bt(int id, int l, int r) {
tr[id].l = l;
tr[id].r = r;
tr[id].mi = 0x3f3f3f3f;
if (l == r) return;
int mid = (l + r) >> 1;
bt(ls(id), l, mid);
bt(rs(id), mid + 1, r);
}
void add(int id, int pos, int d) {
if (tr[id].l == tr[id].r) {
tr[id].mi = min(tr[id].mi, d);
return;
}
int mid = (tr[id].l + tr[id].r) >> 1;
if (pos <= mid) add(ls(id), pos, d);
else add(rs(id), pos, d);
push_up(id);
}
int ask(int id, int l, int r) {
if (tr[id].l >= l && tr[id].r <= r) return tr[id].mi;
int mid = (tr[id].l + tr[id].r) >> 1;
if (r <= mid) return ask(ls(id), l, r);
else if (l > mid) return ask(rs(id), l, r);
else return min(ask(ls(id), l, mid), ask(rs(id), mid + 1, r));
}
}
long long w() {
sort(e + 1, e + 1 + n, cmp);
long long ans = 0;
for (int i = 1; i <= k - 1; i++) {
ans += (e[i].r - e[i].l);
}
return ans;
}
bool vis[50005];
int main() {
freopen("se.in", "r", stdin);
freopen("se.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
long long ma = 0;
for (int i = 1; i <= n; i++) {
cin >> e[i].l >> e[i].r;
ma = max(ma, e[i].l);
}
sort(e + 1, e + 1 + n);
SEG::bt(1, 1, ma);
for (int i = n; i >= 1; i--) {
int r = SEG::ask(1, e[i].l, e[i].r);
if (r >= e[i].l && r <= e[i].r) vis[i] = true;
SEG::add(1, e[i].l, e[i].r);
}
for (int i = 1; i <= n; i++) {
if (!vis[i]) c[++cnt] = e[i];
else v.push_back(e[i].r - e[i].l);
}
sort(v.begin(), v.end(), greater<long long>());
for (int i = 0; i < v.size(); i++) {
sum[i + 1] = sum[i] + v[i];
}
for (int i = 1; i <= cnt; i++) {
f[i][1] = max(0ll, c[1].r - c[i].l);
}
for (int i = 2; i <= cnt; i++) {
for (int j = 2; j <= min(i, k); j++) {
for (int x = 1; x < i; x++) {
f[i][j] = max(f[i][j], f[x][j - 1] + max(0ll, c[x + 1].r - c[i].l));
}
}
}
long long ans = 0;
for (int i = 1; i <= k; i++) {
ans = max(ans, f[cnt][i] + sum[k - i]);
}
cout << max(ans, w());
return 0;
}