首页 > 其他分享 >[赛记] csp-s模拟11 && 多校A层冲刺NOIP2024模拟赛07

[赛记] csp-s模拟11 && 多校A层冲刺NOIP2024模拟赛07

时间:2024-10-16 17:59:43浏览次数:1  
标签:11 opt 赛记 int tr long ls id 模拟

玩水 (water) 100pts

一道结论题,考场一眼出,结果认为不对,然后被硬控了2h结果打出了个抽象DP然后过了;

赛后发现,这DP和那个结论是等价的。。。

首先考虑只有两个人怎么做,那么我们只需找出一个位置 $ (i, j) $ 满足 $ a_{i + 1, j} = a_{i, j + 1} $ 即可;

那么三个人呢?

设现在有两个满足上述性质的位置 $ (x_1, y_1) \ (x_2, y_2) $,不难想到这三种情况:

  1. $ x_1 < x_2, y_1 < y_2 $;

  2. $ y_1 = y_2, x_1 = x_2 + 1 $;

  3. $ x_1 = x_2, y_1 = y_2 + 1 $;

反之同理;

那么这三种情况就包含了所有情况,因为这玩意是有一个传递性的(就是三条路组成的字符串都要完全相同);

这DP太抽象,所以代码仅供参考

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
int t;
int n, m;
char a[1005][1005];
bool f[1005][1005][5][2];
int main() {
	freopen("water.in", "r", stdin);
	freopen("water.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> t;
	while(t--) {
		cin >> n >> m;
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				for (int k = 1; k <= 3; k++) {
					f[i][j][k][0] = false;
					f[i][j][k][1] = false;
				}
			}
		}
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				cin >> a[i][j];
			}
		}
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				f[i][j][1][0] = true;
			}
		}
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				if (i == 1 && j == 1) continue;
				for (int k = 2; k <= 3; k++) {
					f[i][j][k][0] |= (f[i - 1][j][k][0] | f[i][j - 1][k][0]);	
					if (i + 1 <= n && j - 1 >= 1 && a[i][j] == a[i + 1][j - 1]) {
						f[i][j][k][1] |= f[i][j - 1][k - 1][0];
					}
					if (i - 1 >= 1 && j + 1 <= m && a[i][j] == a[i - 1][j + 1]) {
						f[i][j][k][1] |= f[i - 1][j][k - 1][0];
					}
					if (i - 1 >= 1 && j - 1 >= 1) {
						if (a[i - 1][j] == a[i][j - 1]) {
							f[i][j][k][0] |= (f[i - 1][j - 1][k - 1][1] | f[i - 1][j - 1][k - 1][0]);
						}
					}
				}
			}
		}
		if (f[n][m][3][0]) cout << 1 << '\n';
		else cout << 0 << '\n';
	}
	return 0;
}

限速(speed)100pts

可以说是思维题了;

考虑求出原树的最小生成树,那么如果树边都小于等于 $ k $,就直接输出所有边中最靠近 $ k $ 的边与 $ k $ 的差,否则输出所有树边中大于 $ k $ 的边与 $ k $ 的差之和;

考虑这为什么是对的,因为 Kruskal 本质上就是在尽可能多的选小的边,每次找的都是链接两个连通块的最小的边,而小的边越多越好,所以是对的;

从另一个角度说,Kruskal 不对,当且仅当我们判断连通性舍弃边时出现问题,那我们分情况考虑:

  1. 当舍弃的边 $ > k $ 时,不管我们以前的边的大小如何,舍弃这条边都是更优的;

  2. 当 $ < k $ 时,这两个点已经联通说明其至少一条边直接或间接的联通而且呈链状,那么我们删掉这条边自然是不劣的(因为如果选这条边,其不会带来更多的小边);

所以是对的;

时间复杂度:$ \Theta(m \log m) $,瓶颈在排序;

点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
int n, m;
long long k;
struct sss{
	int f, t;
	long long w;
}e[500005];
bool cmp(sss x, sss y) {
	return x.w < y.w;
}
bool da, xiao;
int fa[500005];
int find(int x) {
	if (x != fa[x]) fa[x] = find(fa[x]);
	return fa[x];
}
long long Kru() {
	long long ans = 0;
	for (int i = 1; i <= n; i++) fa[i] = i;
	int sum = 0;
	for (int i = 1; i <= m; i++) {
		if (sum == n - 1) break;
		int x = find(e[i].f);
		int y = find(e[i].t);
		if (x != y) {
			fa[x] = y;
			ans += e[i].w;
			sum++;
		}
	}
	return ans;
}
int main() {
	freopen("speed.in", "r", stdin);
	freopen("speed.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	cin >> k;
	xiao = da = true;
	for (int i = 1; i <= m; i++) {
		cin >> e[i].f >> e[i].t >> e[i].w;
		if (e[i].w < k) da = false;
		if (e[i].w > k) xiao = false;
	}
	if (xiao) {
		long long ma = 0;
		for (int i = 1; i <= m; i++) {
			ma = max(ma, e[i].w);
		}
		cout << k - ma;
		return 0;
	}
	if (da) {
		sort(e + 1, e + 1 + m, cmp);
		long long now = Kru();
		cout << now - (n - 1) * k;
		return 0;
	}
	sort(e + 1, e + 1 + m, cmp);
	for (int i = 1; i <= n; i++) fa[i] = i;
	int sum = 0;
	long long mi = 0x3f3f3f3f3f3f3f3f;
	for (int i = 1; i <= m; i++) {
		mi = min(mi, 1ll * abs(k - e[i].w));
	}
	long long ans = 0;
	for (int i = 1; i <= m; i++) {
		if (sum == n - 1) break;
		int x = find(e[i].f);
		int y = find(e[i].t);
		if (x != y) {
			fa[x] = y;
			if (e[i].w > k) ans += (e[i].w - k);
			sum++;
		}
	}
	if (ans == 0) cout << mi;
	else cout << ans;
	return 0;
}

酒鬼 (drunkard)27pts

赛时打了两个特殊性质27pts;

考虑正解,其实是一堆特判?

是的,所以不太想写了

溜了。。。

点击查看代码
#include <iostream>
#include <cstdio>
#include <string>
#include <set>
#include <cmath>
using namespace std;
int n, m;
set<pair<int, int> > s;
int main() {
	freopen("drunkard.in", "r", stdin);
	freopen("drunkard.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	string ss;
	int x, t;
	int ma = 2e9, pos = 2e9, ls = -1;
	bool vis = true;
	for (int i = 1; i <= m; i++) {
		cin >> ss;
		if (ss == "clue") {
			cin >> x >> t;
			if (x - 1 > t) {
				vis = false;
				continue;
			}
			if (x > 1) {
				ma = min(ma, t - (x - 1));
				pos = min(pos, t);
			}
			s.insert({t, x});
			auto ptr = s.find({t, x});
			if (ptr != s.begin() && next(ptr) != s.end() && ls == (next(ptr) -> first)) ls = -1;
			if (ptr != s.begin()) {
				auto pp = prev(ptr);
				if ((t - pp -> first) < abs(x - pp -> second)) {
					vis = false;
					continue;
				}
				if ((pp -> first + pp -> second) % 2 != (x + t) % 2) ls = max(ls, t);
			}
			if (next(ptr) != s.end()) {
				auto pp = next(ptr);
				if ((pp -> first - t) < abs(x - pp -> second)) {
					vis = false;
					continue;
				}
				if ((pp -> first + pp -> second) % 2 != (x + t) % 2) ls = max(ls, pp -> first);
			}
			if (pos < ls) {
				vis = false;
				continue;
			}
		}
		if (ss == "min") {
			if (!vis) {
				cout << "bad" << '\n';
				continue;
			}
			if (ls != -1) {
				auto ptr = s.lower_bound({ls, 0});
				cout << ((prev(ptr) -> first) + 1) << '\n';
			} else {
				if (s.empty()) cout << 0 << '\n';
				else {
					auto ptr = s.begin();
					cout << (ptr -> first + ptr -> second + 1) % 2 << '\n';
				}
			}
		}
		if (ss == "max") {
			if (!vis) {
				cout << "bad" << '\n';
				continue;
			}
			if (ma == 2e9) cout << "inf" << '\n';
			else cout << ma << '\n';
		}
	}
	return 0;
}

距离(distance)40pts

赛时居然没有打出 $ \Theta(n^2) $ 暴力?还是太菜

对于暴力,$ \Theta(n^3) $ 很好打,$ \Theta(n^2) $ 考虑枚举每一对点,计算其贡献并加到其对应的祖先上,这个可以用树上差分解决(实在不行套个树剖也行);

考虑正解,首先如果把 $ \min $ 换成 $ \max $ 的话,那么我们把 $ (a_x, b_x) \ (a_y, b_y) $ 看成两个点,所求即它们两个的切比雪夫距离

所以用 $ \min-\max $ 容斥,即 $ a + b = \min(a, b) + \max(a, b) $,将要求的式子 $ \min(|a_x - a_y|, |b_x - b_y|) $ 换成 $ |a_x - a_y| + |b_x - b_y| - \max(|a_x - a_y|, |b_x - b_y|) $;

发现后面的那个还是不好求,所以考虑将切比雪夫距离转化为曼哈顿距离

设 $ p_i = \frac{a_i + b_i}{2} \ q_i = \frac{a_i - b_i}{2} $,则 $ \max(|a_x - a_y|, |b_x - b_y|) = |p_x - p_y| + |q_x - q_y| $;

证明展开 $ \max(|a_x - a_y|, |b_x - b_y|) $ 中的绝对值即可(或者考虑曼哈顿距离转化为切比雪夫距离,然后逆推即可);

所以原式就转化成了 $ |a_x - a_y| + |b_x - b_y| - |p_x - p_y| + |q_x - q_y| $,那么我们只需处理 $ 4 $ 个形如 $ |a - b| $ 的问题即可;

一种比较容易的想法是合并(毕竟好像只能从下往上合并),有两种,一种启发式合并,一种线段树合并,后一种比较好想,且时间复杂度较优;

那么我们的问题就转化为了如何在值域线段树上进行 push_up 操作;

首先离散化,因为有小数,所以可以都乘 $ 2 $;

考虑左对右的对答案的贡献,可以发现其贡献为 $ cnt_l \times sum_r - cnt_r \times sum_l $,其中 $ cnt $ 代表数出现的次数,$ sum $ 代表数的和;

这是大数减小数,反之同理,要乘 $ 2 $,但因为我们离散化的时候已经乘了,所以就不用乘了(居然形成了闭环!我现在才发现);

所以我们就维护完了;

因为这个题卡空间,所以要分四次做,并且每次要清空线段树,所以常数很大,时间复杂度 $ \Theta(n \log n) $,挂一个 $ 7, 8 $ 的常数;

点击查看代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const long long mod = 1e9 + 7;
#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 n;
struct sss{
	int t, ne;
}e[2000005];
int h[2000005], cnt;
void add(int u, int v) {
	e[++cnt].t = v;
	e[cnt].ne = h[u];
	h[u] = cnt;
}
long long a[500005], b[500005], c[2000005], ans[500005];
int len;
int rt[500005], tot;
namespace SEG{
	struct sss{
		int ls, rs;
		int cnt;
		long long sum, ans;
	}tr[15000005];
	inline void push_up(int id) {
		tr[id].ans = (tr[tr[id].ls].ans + tr[tr[id].rs].ans) % mod;
		tr[id].ans = (tr[id].ans + (tr[tr[id].ls].cnt * tr[tr[id].rs].sum % mod - tr[tr[id].rs].cnt * tr[tr[id].ls].sum % mod + mod) % mod) % mod;
		tr[id].cnt = (tr[tr[id].ls].cnt + tr[tr[id].rs].cnt) % mod;
		tr[id].sum = (tr[tr[id].ls].sum + tr[tr[id].rs].sum) % mod;
	}
	void add(int &id, int l, int r, int pos, long long val) {
		if (!id) id = ++tot;
		if (l == r) {
			tr[id].cnt++;
			tr[id].cnt %= mod;
			tr[id].sum += val;
			tr[id].sum %= mod;
			return;
		}
		int mid = (l + r) >> 1;
		if (pos <= mid) add(tr[id].ls, l, mid, pos, val);
		else add(tr[id].rs, mid + 1, r, pos, val);
		push_up(id);
	}
	int merge(int x, int y, int l, int r) {
		if (!x || !y) return x + y;
		if (l == r) {
			tr[x].ans += tr[y].ans;
			tr[x].ans %= mod;
			tr[x].sum += tr[y].sum;
			tr[x].sum %= mod;
			tr[x].cnt += tr[y].cnt;
			tr[x].cnt %= mod;
			return x;
		}
		int mid = (l + r) >> 1;
		tr[x].ls = merge(tr[x].ls, tr[y].ls, l, mid);
		tr[x].rs = merge(tr[x].rs, tr[y].rs, mid + 1, r);
		push_up(x);
		return x;
	}
}
void dfs(int x, int fa, int s) {
	for (int i = h[x]; i; i = e[i].ne) {
		int u = e[i].t;
		if (u == fa) continue;
		dfs(u, x, s);
		rt[x] = SEG::merge(rt[x], rt[u], 1, len);
	}
	if (s == 1) SEG::add(rt[x], 1, len, lower_bound(c + 1, c + 1 + len, 2 * a[x]) - c, 2 * a[x]);
	if (s == 2) SEG::add(rt[x], 1, len, lower_bound(c + 1, c + 1 + len, 2 * b[x]) - c, 2 * b[x]);
	if (s == 3) SEG::add(rt[x], 1, len, lower_bound(c + 1, c + 1 + len, a[x] + b[x]) - c, a[x] + b[x]);
	if (s == 4) SEG::add(rt[x], 1, len, lower_bound(c + 1, c + 1 + len, a[x] - b[x]) - c, a[x] - b[x]);
	if (s <= 2) ans[x] = (ans[x] + SEG::tr[rt[x]].ans) % mod;
	else ans[x] = (ans[x] - SEG::tr[rt[x]].ans + mod) % mod;
}
int main() {
	freopen("distance.in", "r", stdin);
	freopen("distance.out", "w", stdout);
	FI(n);
	int x, y;
	for (int i = 1; i <= n - 1; i++) {
		FI(x);
		FI(y);
		add(x, y);
		add(y, x);
	}
	for (int i = 1; i <= n; i++) {
		FI(a[i]);
		FI(b[i]);
		c[++len] = 2 * a[i];
		c[++len] = 2 * b[i];
		c[++len] = a[i] + b[i];
		c[++len] = a[i] - b[i]; 
	}
	sort(c + 1, c + 1 + len);
	len = unique(c + 1, c + 1 + len) - c - 1;
	for (int i = 1; i <= 4; i++) {
		dfs(1, 0, i);
		for (int j = 1; j <= n; j++) rt[j] = 0;
		for (int j = 1; j <= tot; j++) {
			SEG::tr[j].ls = SEG::tr[j].rs = SEG::tr[j].cnt = SEG::tr[j].sum = SEG::tr[j].ans = 0;
		}
		tot = 0;
	}
	for (int i = 1; i <= n; i++) {
		FO(ans[i]);
		FO('\n');
	}
	Flush;
	return 0;
}

标签:11,opt,赛记,int,tr,long,ls,id,模拟
From: https://www.cnblogs.com/PeppaEvenPig/p/18470482

相关文章

  • 10.11
    软件构造第四次作业 一.多选题(共6题,46.1分)1. (多选题)表驱动编程中,表象查询的方法包括:A.阶梯访问B.直接访问C.索引访问D.表项的内容我的答案: ABC:阶梯访问;直接访问;索引访问; 2. (多选题)断言分为如下几类:A.不变断言B.前置断言C.后置断言......
  • AI预测福彩3D采取888=3策略+和值012路或胆码测试10月16日新模型预测第112弹
              经过100多期的测试,当然有很多彩友也一直在观察我每天发的预测结果,得到了一个非常有价值的信息,那就是9码定位的命中率非常高,100多期一共只错了12次,这给喜欢打私房菜的朋友提供了极高价值的预测结果~当然了,大部分菜友还是走的正常渠道,因此,得想办法进行缩水,......
  • DevEco Studio:模拟器的更多扩展能力
    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(MaoistLearning)➤博客园地址:为敢技术(https://www.cnblogs.com/strengthen/ )➤GitHub地址:https://github.com/strengthen➤原文地址:https://www.cnblogs.com/strengthen/p/......
  • DevEco Studio:启动和关闭模拟器
    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(MaoistLearning)➤博客园地址:为敢技术(https://www.cnblogs.com/strengthen/ )➤GitHub地址:https://github.com/strengthen➤原文地址:https://www.cnblogs.com/strengthen/p/......
  • 从Windows 11 23H2升级至24H2后,Git操作提示文件所有权错误的3种有效解决方案
    从Windows1123H2升级至24H2后,Git操作提示文件所有权错误的3种有效解决方案在升级至Windows1124H2后,使用gitadd等命令时,可能会遇到如下错误提示:Error:libgit2returned:repositorypath'D:/repo/it-tools'isnotownedbycurrentuser.Toaddanexceptionforth......
  • P1307 [NOIP2011 普及组] 数字反转
    P1307[NOIP2011普及组]数字反转提交483.96k通过196.21k时间限制1.00s内存限制128.00MB提交答案加入题单做题计划(首页)个人题单团队题单保存题目提供者CCF_NOI难度入门历史分数0 提交记录  查看题解标签NOIp普及组2011 查看算法标签进入讨论版相关讨论......
  • Java 初学 day11
    Java11常用类练习importjava.util.Scanner;/*字符串反转举例:键盘录入”abc”输出结果:”cba”*/publicclassStringTest1{publicstaticvoidmain(String[]args){Scannersc=newScanner(System.in);System.out.println......
  • YOLO11在训练和导出时的一些参数设置
    train时,imsz只能设置成1个整数。如果设置成数组,会提示:updatingto'imgsz=640'.'train'and'val'imgszmustbeaninteger,while'predict'and'export' 图像会以较长的边等比例缩放到指定的整数,然后较短的边的两侧填充114到指定的整数尺寸。即最终会是一个正方形,原图缩放......
  • 跨店每满300元减50元怎么算 2024京东双11满减规则
    1、预售商品参加跨店满减的门槛=定金+尾款(尾款=预售价-定金-付定立减额度)。举个例子:商品的预售价格是250元,商品定金是50元,付定立减30元,则尾款价格为170元,参与跨店满减的凑单的金额为220元(50+170)。京东双十一超级红包领取地址http://www.adiannao.cn/82、跨店满减的凑单......
  • 一个简单的价格模拟工具
    模拟交易价格对于量化分析建模很重要,下面是一个简单的价格模拟工具:首先,要找到标的的价格波动属性,而标的的波动每天都不一样,下面这个代码可以直观地绘制出价格波动的变化情况:defcalculate_volatility():#取日线数据data=get_data(symbol="rb",duration_seconds......