2022.3.26

A

在这里插入图片描述
二维dp[i][j]表示第i位选择j情况(0/1)的所有方案数。

#include<iostream>
using namespace std;
const int N=1e5+10,mod=5000011;
int f[N],n,k;
void solve()
{
  cin>>n>>k;
  for(int i=1;i<=k+1;i++) f[i]=i+1;
  for(int i=k+2;i<=n;i++)
  {
	f[i]=f[i-k-1]+f[i-1];  
    f[i]%=mod;
	}
  cout<<f[n]<<endl;
}
int main()
{
solve();
}

B

在这里插入图片描述
带权值并查集,merge的时候维护并查集顶端的数值,find的时候更新merge时更改的值。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e6 + 10;
int pre[N], up[N], s[N], n;
int find(int x)
{
	if (x == pre[x])
	{
		return x;
	}
	else
	{
		int k = pre[x];
		pre[x] = find(pre[x]);
		up[x] += up[k];
		s[x] = s[pre[x]];
		return pre[x];
	}
}
void solve()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		pre[i] = i;
		s[i] = 1;
		up[i] = 0;
	}
	for (int i = 1; i <= n; i++)
	{
		char a;
		cin >> a;
		if (a == 'M')
		{
			int l, r;
			cin >> l >> r;
			l = find(l), r = find(r);
			pre[r] = l;
			up[r] = s[l];
			up[l] = 0;
			s[l] += s[r];
		}
		else
		{
			int x;
			cin >> x;
			int y = find(x);
			cout << s[y] - up[x] - 1 << endl;
		}
	}
}
int main()
{
	solve();
}

C

在这里插入图片描述
这题运用了lca虽然给的是一张图,但是题目所给条件说明了图一定联通,因此在不考虑重复的边和删去的边的情况下,也一定是连通的,利用这一点建树,dfs预处理lca,如果插入边后成环,就根据插入边两端点的lca路径,经过点权值设置为0,查询两端点有多少航桥时,利用两端点的求lca过程,计数有多少边权值为1即可。

#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
const int N = 4e4 + 10;
vector<int> edge[N];
int fa[N], vis[N], dep[N], pre[N], idx, v[N];
int n, m;
pair<int, int> e[400010];
map<pair<int, int>, int> mp;
struct node
{
	int op, a, b;
}ask[N];
int find(int x)
{
	return x == pre[x] ? x : pre[x] = find(pre[x]);
}
void dfs(int u, int far, int d)
{
	fa[u] = far;
	dep[u] = d;
	for (auto x : edge[u])
	{
		if (x == far) continue;
		dfs(x, u, d + 1);
	}
}
void change(int a, int b)
{
	if(dep[a] < dep[b]) swap(a, b);
	while (dep[a] != dep[b])
	{
		v[a] = 0;
		a = fa[a];
	}
	while (a != b)
	{
		v[a] = 0, v[b] = 0;
		a = fa[a], b = fa[b];
	}
}
int query(int a, int b)
{
	int res = 0;
	if (dep[a] < dep[b]) swap(a, b);
	while (dep[a] != dep[b])
	{
		res += v[a];
		a = fa[a];
	}
	while (a != b)
	{
		res += v[a] + v[b];
		a = fa[a], b = fa[b];
	}
	//cout << res << endl;
	return res;
}
void solve()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		pre[i] = i;
		v[i] = 1;
	}
	for (int i = 1; i <= m; i++)
		cin >> e[i].first >> e[i].second;
	int op;
	while (cin >> op)
	{
		if (op == -1)break;
		int a, b;
		cin >> a >> b;
		
		if (op == 0) mp[{a, b}] = 1;
		idx++;
		ask[idx].op = op, ask[idx].a = a, ask[idx].b = b;
	}
	//puts("111");
	for (int i = 1; i <= m; i++)
	{
		int a = e[i].first, b = e[i].second;
		if (mp[{a, b}] == 1) continue;
		int fa = find(a), fb = find(b);
		if (fa != fb)
		{
			pre[fa] = fb;
			edge[a].push_back(b);
			edge[b].push_back(a);
			mp[{a, b}] = 1;
		}
	}
	//puts("dfs");
	dfs(1, -1, 0);
	fa[1] = 1;
	for (int i = 1; i <= m; i++)
	{
		int a = e[i].first, b = e[i].second;
		if (mp[{a, b}] == 1) continue;
		change(a, b);
	}
	vector<int> ans;
	for (int i = idx; i; i--)
	{
		if (ask[i].op == 1)
			ans.push_back(query(ask[i].a, ask[i].b));
		else change(ask[i].a, ask[i].b);
	}
	reverse(ans.begin(), ans.end());
	for (auto x : ans) cout << x << endl;
}
int main()
{
	solve();
}

I

在这里插入图片描述
要想知道这个房间是否有人,一定需要知道到房间i为止的前缀和以及到房间i-1为止的前缀和,题目要求所有房间的值。
而这些不同房间的前缀和也可以进行合并与差,得到一个新的,这题本质就是求n个房间,0到n的前缀和点的最小生成树。

#include<iostream>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
const int N = 3e6 + 10;
int pre[N];
struct node
{
	int x, y, val;
}e[N];
int cmp(node a, node b)
{
	return a.val < b.val;
}
int find(int x)
{
	return x == pre[x] ? x : pre[x] = find(pre[x]);
}
void solve()
{
	int n, cnt = 0, ans = 0, sum = 0;
	cin >> n;
	for (int i = 0; i <= n; i++)pre[i] = i;
	for (int i = 1; i <= n; i++)
	{
		for (int j = i; j <= n; j++)
		{
			e[++cnt].x = i - 1;
			e[cnt].y = j;
			cin >> e[cnt].val;
		}
	}
	sort(e + 1, e + 1 + cnt, cmp);
	for (int i = 1; i <= cnt; i++)
	{
		int x = e[i].x, y = e[i].y;
		x = find(x), y = find(y);
		if (x != y)
		{
			ans += e[i].val;
			pre[x] = y;
			//sum++;
		}
	}
	cout << ans << endl;

}
signed main()
{
	solve();
}

K在这里插入图片描述

这题有一点二分的思想,每次把图形分割成两部分,直到最小成分即i-i+1的组合,对于最小组成,我们有三种操作,删去最左边的点,删去最右边的点,左右都删。我们从最大图形二分递归到最小组成,在回溯过程中,进行状态转移,
第一个操作从左边开始删的状态: 左儿子和右儿子均从左边删顶点, 或者 左儿子左右删顶点+右儿子从左边删顶点
第二个操作从右边开始删的状态: 左儿子和右儿子均从右边删顶点, 或者 右儿子左右删顶点+左儿子从右边删顶点
第三个操作左右两边同时删:左儿子和右儿子均从左右删顶点, 或者 左儿子从左边删顶点+右儿子从右边删顶点

建边的主要目的是为了维护二分过程,所以只连j比i大的边为i->j,。

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 2e5 + 10;
struct node
{
	int l, r, lr;
}dp[N];
vector<int> to[N];
int idx, num[N], ls[N], rs[N];
int dfs(int l, int r)
{
	//cout << l << " " << r << endl;
	int p = ++idx;
	if (l == r - 1)
		dp[p].l = 1, dp[p].r = 1, dp[p].lr = 2;
	else
	{
		int mid = to[l][++num[l]];

		ls[p] = dfs(l, mid);
		rs[p] = dfs(mid, r);

		dp[p].l = min(dp[ls[p]].l + dp[rs[p]].l, dp[ls[p]].lr + dp[rs[p]].l - 1);
		dp[p].r = min(dp[ls[p]].r + dp[rs[p]].r, dp[ls[p]].r + dp[rs[p]].lr - 1);
		dp[p].lr = min(dp[ls[p]].l + dp[rs[p]].r, dp[ls[p]].lr + dp[rs[p]].lr - 1);
	}
	return p;
}
void solve()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n * 2 - 3; i++)
	{
		int a, b;
		cin >> a >> b;
		if (a > b) swap(a, b);
		to[a].push_back(b);
	}
	for (int i = 1; i <= n; i++)
		sort(to[i].begin(), to[i].end(), greater<int>());
	dfs(1, n);
	int ans = min(dp[1].l,dp[1].r);
	ans = min(ans, dp[1].lr);
	cout << ans << endl;
}
signed main()
{
	solve();

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值