[賽記] 多校A層衝刺NOIP2024模擬賽15

Peppa_Even_Pig發表於2024-10-30

追逐遊戲 (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;
}

相關文章