[赛记] 多校A层冲刺NOIP2024模拟赛15

追逐游戏 (chase) 50pts

比较卡常

考虑二分答案,发现我们只需要在知道答案的情况下找出终点即可,所以用倍增找出终点,最后判断一下合不合法即可;

时间复杂度:Θ(nlog2n),常数很大,赛时被卡常了;

当然也可以分讨做到 Θ(nlogn) 复杂度,这里不细讲;

点击查看代码
#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乱搞搞就对了。。。

1m1 的Hash值赋成随机数,m 的Hash值赋成它们的和的相反数,然后做一遍前缀和统计一下相同数的个数然后组合一下即可;

时间复杂度:Θ(Tnlogn),瓶颈在排序;

点击查看代码
#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;

分两种情况:有不交的区间,没有不交的区间;

前者直接输出前 k1 大,后者直接DP,设 fi,j 表示前 i 个线段分成 j 段的最大权值,因为只会选连续的一段,所以直接从 fk,j1 转移而来即可;

最后前缀 max 优化一下,时间复杂度:Θ(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;
}
long long maa[50005];
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];
	}
	maa[0] = c[1].r;
	for (int i = 1; i <= cnt; i++) {
		for (int j = 1; j <= min(i, k); j++) {
			f[i][j] = max(f[i][j], maa[j - 1] - c[i].l);
		}
		for (int j = 1; j <= min(i, k); j++) {
			maa[j] = max(maa[j], f[i][j] + c[i + 1].r);
		}
	}
	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;
}
posted @   Peppa_Even_Pig  阅读(32)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示