Nov 12

今天爆 0 了,没有什么好讲的,技不如人罢了。感觉自己需要放假。

T1 CF2117F

场上想出来了一半。

首先明显的,最多有两个叶子节点。

我于是尝试按照有几个叶子进行讨论。

如果只有一个叶子,说明这个图就是一条链。

我们这个时候随便填,因为链的结构,我们的 s 是从小往上单调不降的。

之后再考虑 “人” 字型的结构,我们发现两个子节点分别是 1/2 一共有两种情况,且如果一个一个往上填,距离两个的 lca 近的那个到 lca 只能放满 2, 另一个对应的也会有一部分是 2, lca 上边就是之前的情况了。

我们分别讨论最长链的叶子放的是 1/2,如果是 2,我们下一个必须是 2, 如果是 1 就无所谓。

直接列式子求就行了。

代码↓

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
const int MN=1e6+116;
struct Node{
	int nxt, to;
}node[MN];
int head[MN], tottt;
int siz[MN], n;
int quick_power(int a, int b){
	int res=1;
	while(b){
		if(b&1) res=(res*a)%mod;
		a=(a*a)%mod; b>>=1;
	}
	return res;
}
void insert(int u, int v){
	node[++tottt].to=v;
	node[tottt].nxt=head[u];
	head[u]=tottt;
}
int lca_len;
vector<int> lens;
void dfs(int u,int fa,int dep){
	int deg=0;
	for(int i=head[u];i;i=node[i].nxt){
		int v=node[i].to;
		if(v==fa) continue;
		deg++;
		dfs(v,u,dep+1);
	}
	if(deg>1) lca_len=dep;
	if(deg==0) lens.push_back(dep);
}
void Solve(){
	cin>>n; tottt=0;
	for(int i=1;i<=n;++i) head[i]=0;
	for(int i=1;i<n;++i){
		int u,v; cin>>u>>v;
		insert(u,v); insert(v,u);
	}
	lens.clear(); lca_len=0;
	dfs(1,0,1);
	if((int)lens.size()>2){
		cout<<0<<'\n';
		return;
	}
	if((int)lens.size()==1){
		cout<<quick_power(2,n)<<'\n';
		return;
	}
	int diff=abs(lens[0]-lens[1]);
	if(diff==0){
		cout<<quick_power(2,lca_len+1)%mod<<'\n';
	}else{
		cout<<(quick_power(2,lca_len+diff-1)+quick_power(2,lca_len+diff))%mod<<'\n';
	}
	return;
}
signed main(){
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int T; cin>>T;
	while(T--) Solve();
	return 0;
}

T2 CF2117G

这个就是一道正正常常的图论题。

从小到大加边,这个时候我们每加一条就是最大值,并查集维护 1 到 n 有没有联通和联通快的最小边权,每次如果联通取答案就行了。

代码↓

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MN=1e6+116;
const int inf=0x3f3f3f3f3f3f3f3f;
struct DSU{
	int father[MN], minn[MN];
	void init(int n){
		for(int i=0; i<n+1; ++i) father[i]=i;
		for(int i=0; i<n+1; ++i) minn[i]=inf;
	}
	int find(int x){
		if(father[x]!=x) father[x]=find(father[x]);
		return father[x];
	}
	void merge(int u, int v, int w){
		u=find(u), v=find(v);
		minn[u]=min(min(minn[u],minn[v]),w);
		father[v]=u;
	}
}dsu;
int n, m;
struct Side{
	int u, v, w;
	bool operator <(const Side &o)const{
		return w<o.w;
	}
};
vector <Side> side;
void Read(){
	cin>>n>>m; dsu.init(n); side.clear();
	for(int i=1; i<=m; ++i){
		int u, v, w; cin>>u>>v>>w;
		side.push_back({u,v,w});
	}
	sort(side.begin(),side.end());
	return;
}
void Solve(){
	Read(); int ans=inf;
	for(auto tmp:side){
		dsu.merge(tmp.u,tmp.v,tmp.w);
		if(dsu.find(1)==dsu.find(n)){
			ans=min(ans,tmp.w+dsu.minn[dsu.find(1)]);
			break;
		}
	}
	cout<<ans<<'\n';
	return;
}
signed main(){
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int T; cin>>T; while(T--) Solve();
	return 0;
}

T3 CF2112E

实事求是讲,我并没有怎么看到过这种套路。

我们先考虑如果知道一棵树,我们怎么求解答案。

\(f[u]=\prod_{v\in son[u]} (f[v]+2)\)

就是考虑合并的时候会多出来把整棵子树全部染成 b/y。

知道了答案的计算就好了。

我们设 \(g[i]\) 表示答案为 \(i\) 时的最少点数。

\(g[i]=min_{j|i,j>2}(g[\frac{i}{j}]+g[j-2])\)

也就是枚举这个答案是怎么由两个子树拼接出来的。

非常巧妙的思想,根据调和数的知识,这个是 \(O(nlogn)\) 左右的。

代码↓

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MN=5e5+116;
int dp[MN];
void Pre(){
	dp[1]=1;
	for(int i=2; i<MN; ++i){
		dp[i]=0x3f3f3f3f3f3f3f3f;
		for(int j=1; j*j<=i; ++j){
			if(i%j) continue;
			if(j>2) dp[i]=min(dp[i],dp[i/j]+dp[j-2]);
			if(i/j>2) dp[i]=min(dp[i],dp[j]+dp[i/j-2]);
		}
	}
	return;
}
void Solve(){
	Pre(); int T; cin>>T;
	while(T--){
		int val; cin>>val;
		if(dp[val]==0x3f3f3f3f3f3f3f3f) cout<<-1<<'\n';
		else cout<<dp[val]<<'\n';
	}
}
signed main(){
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	Solve();
	return 0;
}

真的是很好的题目,但是我并没有做的很好。

posted @ 2025-11-12 13:00  BaiBaiShaFeng  阅读(3)  评论(0)    收藏  举报
Sakana Widget右下角定位