首页 > 其他分享 >2024.7.20 模拟赛总结

2024.7.20 模拟赛总结

时间:2024-07-20 21:06:56浏览次数:10  
标签:le 20 2024.7 int dis edges mod 模拟 关键点

T1 lcd

Statement:

给定 \(n (1 \le n \le 10^8)\),问有多少对 \((i, j) (1 \le i, j \le n)\) 满足 \(\frac{xy}{\gcd(x, y)^2} \le 3\)。

Solution:

简单题。令 \(x' = \frac{x}{\gcd(x, y)}, y' = \frac{y}{\gcd(x, y)}\),枚举 \((x', y')\) 并计算即可。

T2 cut

Statement:

给定一颗有 \(n(1 \le n \le 5 \times 10^5)\) 个节点的树,其中有 \(n - 1\) 条边,其中第 \(i\) 条边连接 \((u_i, v_i)\)。问断掉第 \(i\) 条边后,\(u_i, v_i\) 所处非空连通子图个数分别为多少。答案对 \(998244353\) 取模。

Solution:

比较简单的换根 \(\rm DP\)。

先考虑如何求出整棵树的非空连通子图个数,\(\rm DP\) 容易解决。设 \(f_{u, 0 / 1}\) 是不选/选 \(u\) 时,以 \(u\) 为根子树的非空连通子图个数。显然 \(f_{u, 0} = \sum_{v \in subtree(u)} {f_{v, 0} + f_{v, 1}}\),\(f_{v, 1} = \prod_{v \in subtree(u)}{1 + f_{v, 1}}\)。

考虑切掉 \((u, v)\) 这条边后的影响,其中 \(u\) 是 \(v\) 的父亲,显然 \(v\) 中的答案是 \(f_{v, 1} + f_{v, 0}\)。现在计算 \(u\) 所处连通块的答案。显然我们需要割掉 \(v\) 对 \(u\) 带来的贡献,并加入 \(u\) 父亲割掉 \(u\) 后的贡献。于是考虑换根:设 \(g_{u, 0 / 1}\) 是不选/选 \(u\) 时,以 \(u\) 为根子树的非空连通子图个数。转移容易,但是有可能存在一个 \(f_{u, 0} + 1\) 为 \(mod\) 的倍数,不能使用逆元剔除贡献而是只能维护前缀和后缀积并将它们乘起来。

qwq
#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N = 5e5 + 10, mod = 998244353;

struct edge{
	int v, id, next;
}edges[N << 1];
int head[N], idx;
int f[N][2], g[N][2];

struct Edge{
	int u, v, ansu, ansv;
}ans[N];
vector<int> pre[N], suf[N];

int qpow(int x, int y){
	int ret = 1; x %= mod;
	while(y){
		if(y & 1) ret = ret * x % mod;
		x = x * x % mod; y >>= 1;
	}
	return ret;
}

void add_edge(int u, int v, int id){
	edges[++idx] = {v, id, head[u]};
	head[u] = idx;
}
int n;

void dfs1(int u, int fa){
	f[u][1] = 1;
	for(int i = head[u]; i; i = edges[i].next){
		int v = edges[i].v; if(v == fa) continue;
		dfs1(v, u); f[u][0] = (f[u][0] + ((f[v][1] + f[v][0]) % mod)) % mod;
		f[u][1] = f[u][1] * ((1 + f[v][1]) % mod) % mod; pre[u].push_back(1 + f[v][1]); suf[u].push_back(1 + f[v][1]);
	}
	for(int i = suf[u].size() - 2; i >= 0; i--) suf[u][i] = (suf[u][i + 1] * suf[u][i]) % mod;
	for(int i = 1; i < suf[u].size(); i++) pre[u][i] = (pre[u][i - 1] * pre[u][i]) % mod;
	suf[u].push_back(1);
//	cout << u << " " << f[u][1] << " " << f[u][0] << "\n";
}

void dfs2(int u, int fa, int id){
	int f0 = (g[fa][0] - ((f[u][0] + f[u][1]) % mod) + mod) % mod, f1 = id;
	if(u != 1) g[u][0] = (f[u][0] + (f0 + f1)) % mod, g[u][1] = (f[u][1] * (1 + f1)) % mod;
	else g[u][1] = f[u][1], g[u][0] = f[u][0], f1 = 0; int tot = 0;
	for(int i = head[u]; i; i = edges[i].next){
		int v = edges[i].v; if(v == fa) continue; 
		int fu0 = (g[u][0] - ((f[v][0] + f[v][1]) % mod) + mod) % mod, fu1 = (((1 + f1) * (tot ? pre[u][tot - 1] : 1) % mod) * suf[u][tot + 1] % mod) % mod;
		int ansv = (f[v][1] + f[v][0]) % mod, ansu = (fu0 + fu1) % mod;
//		cout << u << " " << v << " " << f[u][0] << " " << f[u][1] << " " << f0 << " " << f1  << " " << fu0 << " " << fu1 << "\n";
		if(u == ans[edges[i].id].u) ans[edges[i].id].ansu = ansu, ans[edges[i].id].ansv = ansv;
		else ans[edges[i].id].ansu = ansv, ans[edges[i].id].ansv = ansu;
		dfs2(v, u, fu1); tot++;
	}
}

signed main(){
	//freopen("3.in", "r", stdin);	
	//freopen("my.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin >> n;
	for(int i = 1; i < n; i++){
		int x, y; cin >> x >> y;
		add_edge(x, y, i); add_edge(y, x, i); 
		ans[i] = {x, y, 0, 0};
	}
	dfs1(1, 0); dfs2(1, 0, 0);
	for(int i = 1; i < n; i++) cout << ans[i].ansu << " " << ans[i].ansv << "\n";

	//system("pause");
	return 0;
}
/*
5
1 2
2 3
3 4
4 5
*/

T3 为了你唱下去

Statement:

给定一张有 \(n (1 \le n \le 10^5)\) 个点,有 \(m (1 \le 5 \times 10^5)\) 条带边权的边的无向图,其中 \(1 - c\) 是关键点。走过一条边会消耗该边边权大小的能量,如果当前能量小于边权则无法经过该点,但是到达关键点后又会将能量恢复到 \(r\)。现在问有多少非关键点可以从一个关键点出发,通过一条至少经过 \(k\) 个关键点的路径后到达。

\(30\)% 数据满足 \(n \le 500\)。

Solution:

图论好题。

首先容易发现两个关键点能够相互到达当且仅当两个点的最短距离小于等于 \(r\)。于是我们可以将关键点分为一些互不能到达且集合内部可以相互到达的集合。对于一个集合,我们可以先从任意点出发,走完里面的关键点后再去另外一个关键点。再由这些集合定义可知,只有集合大小大于等于 \(k\) 的集合才对答案有贡献。对于一个非关键点,若它与集合任一点的最短路径长度小于等于 \(r\),则该节点可以通过路径走到。\(\rm Floyd\) 可以拿到 \(30pts\) 的高分。

考虑优化合并集合的过程。我们首先将所有关键点的距离标记为 \(0\),跑一边 \(\rm Dijkstra\),求出所有非关键点距离最近的一个关键点。枚举每一条边,对于一条边 \(i\),若 \(w_i + dis[u] + dis[v] \le r\),合并 \(u, v\) 所在的集合,并查集容易做到 \(O(m \log n)\)。

接着我们将所有所在集合大小大于等于 \(k\) 的集合的关键点的 \(dis\) 设为 \(0\),再跑一遍 \(\rm Dijkstra\) ,计算 \(dis \le r\) 的点数即可。

qwq
#include<bits/stdc++.h>
#define int long long
#define pir pair<int, int>
using namespace std;

const int N = 1e5 + 10, M = 5e5 + 10, INF = 1e18;

int n, m, c, r, k, fa[N], siz[N], dis[N], vis[N], from[N];
struct edge{
	int v, w, next;
}edges[M << 1];
int head[N], idx;

void add_edge(int u, int v, int w){
	edges[++idx] = {v, w, head[u]};
	head[u] = idx;
}

int findfa(int x){return fa[x] = ((x == fa[x]) ? x : findfa(fa[x]));}
void merge(int x, int y){
	int fx = findfa(x), fy = findfa(y);
	if(fx != fy){fa[fx] = fy; siz[fy] += siz[fx];}
}
void buildc(){
	for(int i = 1; i <= n; i++) dis[i] = INF;
	priority_queue<pir, vector<pir>, greater<pir> >Q;
	for(int i = 1; i <= c; i++) dis[i] = 0, Q.push({0, i}), from[i] = i;
	while(!Q.empty()){
		pir p = Q.top(); Q.pop();
		int u = p.second, dist = p.first;
		if(vis[u]) continue; vis[u] = true;
		for(int i = head[u]; i; i = edges[i].next){
			int v = edges[i].v;
			if((!vis[v]) && dis[v] > dist + edges[i].w && dist + edges[i].w <= r){
				dis[v] = dist + edges[i].w; from[v] = from[u];
//				cout << v << " " << u << " " << dist + edges[i].w << "\n";
				Q.push({dis[v], v});
			}
		}
	}
	for(int u = 1; u <= n; u++){
		for(int i = head[u]; i; i = edges[i].next){
			int v = edges[i].v;
			if(dis[v] + edges[i].w + dis[u] <= r) merge(from[u], from[v]);
		}
	}
}

void getans(){
	for(int i = 1; i <= n; i++) dis[i] = INF, vis[i] = false;
	priority_queue<pir, vector<pir>, greater<pir> >Q;
	for(int i = 1; i <= c; i++) if(siz[findfa(i)] >= k) dis[i] = 0, Q.push({0, i});
//	for(int i = 1; i <= c; i++) cout << siz[findfa(i)] << "\n";
	while(!Q.empty()){
		pir p = Q.top(); Q.pop();
		int u = p.second, dist = p.first;
		if(vis[u]) continue; vis[u] = true;
		for(int i = head[u]; i; i = edges[i].next){
			int v = edges[i].v; if(edges[i].w > r) continue;
			if((!vis[v]) && dis[v] > dist + edges[i].w){
				dis[v] = dist + edges[i].w;
				Q.push({dis[v], v});
			}
		}
	}
	int ans = 0;
	for(int i = c + 1; i <= n; i++) if(dis[i] <= r) ans++;
	cout << ans << "\n";
	for(int i = c + 1; i <= n; i++) if(dis[i] <= r) cout << i << " ";
}

signed main(){
	//freopen("ex_sample8.in", "r", stdin);
	//freopen("my.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin >> n >> m >> c >> r >> k;
	for(int i = 1; i <= m; i++){
		int x, y, w; cin >> x >> y >> w;
		add_edge(x, y, w); add_edge(y, x, w);
	}
	for(int i = 1; i <= n; i++) fa[i] = i, siz[i] = 1;
	buildc(); getans();
	
	return 0;
}

标签:le,20,2024.7,int,dis,edges,mod,模拟,关键点
From: https://www.cnblogs.com/little-corn/p/18313700

相关文章

  • 格子玻尔兹曼模拟在对象掩模上运行,模拟应该只是黑色的区域
    我正在尝试使用该方法模拟特斯拉阀门,从此代码开始。问题在于遮罩、边界或反弹未正确应用。如果我反转创建障碍物遮罩的条件,则流量似乎更多地在阀门内部流动。图像:障碍物遮罩模拟输出使用相反遮罩的模拟|||完整代码:......
  • 20240720周赛订正
    20240720周赛订正总结本场比赛没有任何少拿的分。题解T1尺取。#include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;//#defineintlonglong#definelcu<<1#definercu<<1|1#definefifirst#definesesecondconstintN=10......
  • 【2024最新版】Vue前端面试篇,看这一篇就够了
    文章目录Vue常用的指令都有哪些v-bind和v-model的区别Vue2的生命周期有哪些Vue3的生命周期有哪些vue3中创建响应式变量的方法ref和reactive原理vuex有哪些方法vue-router生命周期钩子vue框架和原生JavaScript有什么区别对于提升项目加载速度和运行效率是怎么做的webpack......
  • 2024 暑假友谊赛 2
    A题目链接思路:枚举每个十字中心点,合法就标记,最后若还剩下点没被标记就NO#include<bits/stdc++.h>usingnamespacestd;#defineintlonglong#definePIIpair<int,int>constintN=1e6+5,mod=998244353,Mod=1e9+7;intdx[4]={-1,0,1,0};intdy[4......
  • $NOI2024$游记
    day-?~day-?考了期末考后打省队集训,刚开始打的很差,bai了几天状态恢复。打进前4了,好吃捏。$day-1$报道日,育才宿舍真难泵,真独立卫浴,全是蚊子受不了,想不通育才学生一间住八个人怎么活下去的(。育才辣椒不辣,开心!没洗澡,不嘻嘻。day0今年竟是NOI四十周年,dzd英雄人物上位救赎ccf(bu......
  • [SDOI2011] 拦截导弹
    这是CDQ分治优化1D/1D动态规划的模板题(1D/1D动态规划的概念见OI-wiki)一般来说,优化的1D/1D/动态规划,在转移的时候是由不等式作为条件的,所以可以像这样转化为三维偏序用线段树进行如下维护:1.维护区间最大值2.查询区间最大值的某一数组的和代码见下(一定要学会将数组翻转的操作)#......
  • 暑假第三周总结(7.15-7.20)
    这周做了什么继续学习JAVA,做出了城堡游戏点击查看代码//RoompackagecastleV3;importjava.util.HashMap;publicclassRoom{ privateStringdescription;privateHashMap<String,Room>exits=newHashMap<String,Room>();publicRoom(String......
  • 2024“钉耙编程”中国大学生算法设计超级联赛(1)结题报告1 2 8
    1001循环位移字符串哈希将a展开*2对于每个长度为len_a的序列进行一次hash存储并将其插入set中对于b进行一次哈希对于每个长度为len_a的连续子串进行一次查询点击查看代码#include<bits/stdc++.h>usingnamespacestd;//22222constintN=5e6+10;constintp1......
  • day4 链表-模拟与快慢指针
    目录任务24.两两交换链表中的节点思路19.删除链表的倒数第N个节点思路面试题02.07.链表相交思路142.环形链表II思路总结任务24.两两交换链表中的节点给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节......
  • 暑假集训CSP提高模拟3
    暑假集训CSP提高模拟3\(T1\)P117.abc猜想\(100pts\)原题:[ARC111A]SimpleMath2由题,有\(\dfrac{(a^{b}-a^{b}\bmodc)\bmodc^{2}}{c}\)即为所求。证明设\(\left\lfloor\dfrac{a^{b}}{c}\right\rfloor=\dfrac{a^{b}-a^{b}\bmodc}{c}=kc+r\),其中\(r......