2021牛客寒假算法基础训练营1

2021牛客寒假算法基础训练营1

C-红和蓝

题意:
在这里插入图片描述
思路:
构造题的考虑一般是从特殊到一般,那对于树这种结构,最特殊的无非就是叶子节点,有一个颜色旁必须有一个和他颜色相同的节点,那么叶子节点和它的父节点颜色是必定要相同的。

这样我们就可以用dfs,递归地从下往上逐渐到根的将个节点和它的父节点放在一起,用一个编号记录他们属于第几组,处在相同祖别的节点颜色需要一致,不同就不一致。

如果我们标记的过程中,遇见了当前需要被标记的节点已经被其他点标记过了,就说明这棵树是无法完成构造的,还有一种情况就是构造的最后只剩下根节点一个单独的节点了,那么肯定也是无法构造的。

这样在通过一个dfs标记一下颜色,就完成构造了。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 7;

struct node{
	int next,to;
}edge[MAXN<<1];
int head[MAXN],tot;
void addedge(int u,int v){
	edge[++tot].to = v;
	edge[tot].next = head[u];
	head[u] = tot;
}
int f[MAXN],cnt;
bool flag = 0;
void dfs1(int u,int fa) {//从特殊到一般 先去找 叶子节点
	int son = 0;
	for(int i = head[u];i;i = edge[i].next) {
		int v = edge[i].to;
		if(v == fa) continue;
		son++;
		dfs1(v,u);
	}
	if(son == 0 || !f[u]) {//从叶子节点开始往上标记
		if(f[fa] != 0){ flag = 1;return ; }//代表这个点被其他的点已经赋过值了 是不合法的
		f[u] = f[fa] = ++cnt;//这是一组
	}
}

char ans[MAXN];
void dfs2(int u,int fa) {//统计答案 标号为一组的设为一个颜色
	if(!ans[u]) ans[u] = 'R';
	for(int i = head[u];i;i = edge[i].next) {
		int v = edge[i].to;
		if(v == fa) continue;
		if(f[v] == f[u]) ans[v] = ans[u];
		else ans[v] = (ans[u] == 'B' ? 'R' : 'B');
		dfs2(v,u);
	}
}
int main() {
	int n;scanf("%d",&n);
	int u,v;
	for(int i = 1;i < n;i ++){
		scanf("%d%d",&u,&v);
		addedge(u,v);addedge(v,u);
	}
	dfs1(1,0);
	if(flag == 1 || f[0]) {//这里不可能的情况有两种 中途不可能 也有可能一直做到最后 只剩根节点了 当然也是不合法的构造 所以我们还要看根的父节点
		printf("-1\n");
		return 0;
	}
	dfs2(1,0);
	for(int i = 1;i <= n;i ++) printf(i == n ? "%c\n" : "%c",ans[i]);
	return 0;
}

D-点一成零

题意:
在这里插入图片描述
思路:
很明显用并查集维护一下当前处于联通的1的个数,然后同时记录独立的连通块的个数,然后由题意可知,不同操作顺序也属于不同的方案数,所以最后答案就是联通块个数的阶乘(全排列)* 每个连通块的大小即可。
注意连在一起时的细节。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN = 500 + 7;
const int N = 3e5 + 7;
const int MOD = 1e9 + 7;
char mp[MAXN][MAXN];
int n,k,fa[N],siz[N],dir[4][2] = {{-1,0},{0,1},{1,0},{0,-1}};

int Find(int x) {
	return fa[x] == x ? x : fa[x] = Find(fa[x]);
}

void merge(int x,int y) {//并查集维护联通块大小即可
	int fx = Find(x),fy = Find(y);
	if(fx != fy)
		fa[fy] = fx , siz[fx] += siz[fy];
}

bool check(int x,int y) {
	if(x < 1 || x > n || y < 1 || y > n || mp[x][y] == '0') return false;
	return true;
}

ll q_pow(ll a,ll b) {
	ll res = 1;
	while(b) {
		if(b & 1) res = res * a % MOD;
		a = a * a % MOD;
		b >>= 1;
	}
	return res % MOD;
}
ll inv(ll x){ return q_pow(x,MOD-2); }

int main() {
	scanf("%d",&n);
	getchar();
	for(int i = 1;i <= n;i ++) scanf("%s",mp[i] + 1);
	for(int i = 1;i <= n * n;i ++) { 
		fa[i] = i; siz[i] = 1;
	}
	int cnt = 0;
	ll ans = 1;
	for(int i = 1;i <= n;i ++) {
		for(int j = 1;j <= n;j ++) {
			if(mp[i][j] == '1') {//四个方向的合并
				if(check(i - 1,j)) merge((i - 1) * n + j,(i - 2) * n + j);
				if(check(i,j + 1)) merge((i - 1) * n + j,(i - 1) * n + j + 1);
				if(check(i + 1,j)) merge((i - 1) * n + j,i * n + j);
				if(check(i,j - 1)) merge((i - 1) * n + j,(i - 1) * n + j - 1);
			}
		}
	}
	for(int i = 1;i <= n;i ++) {
		for(int j = 1;j <= n;j ++) {
			if(mp[i][j] == '1' && fa[(i - 1) * n + j] == (i - 1) * n + j) {
				cnt++;
				ans = ans * siz[(i - 1) * n + j] % MOD;
			}
		}
	}
	// cout<<cnt<<'\n';
	for(int i = 1;i <= cnt;i ++) ans = ans * i % MOD;//顺序不同方案数也是不同 全排列一下
	cout<<ans<<endl;
	scanf("%d",&k);
	int x,y;
	while(k--) {
		scanf("%d%d",&x,&y);
		x++,y++;
		if(mp[x][y] == '1') {
			printf("%lld\n",ans);
			continue;
		}
		mp[x][y] = '1';
		int fg = 0;
		for(int i = 0;i < 4;i ++) {
			int xx = x + dir[i][0],yy = y + dir[i][1];
			if(check(xx,yy)) {
				int fx = Find((x - 1) * n + y),fy = Find((xx - 1) * n + yy);
				// cout<<fx<<' '<<fy<<'\n';
				if(fx != fy) {
					ans = ans * inv(siz[fx]) % MOD;
					ans = ans * inv(siz[fy]) % MOD;
					ans = ans * (siz[fx] + siz[fy]) % MOD;
					// cout<<ans<<"***\n";
					if(fg) { 
						ans = ans * inv(cnt) % MOD;
						cnt--;
					}
					fg = 1;
					merge(fx,fy);
				}
			}
		}
		if(!fg) {
			cnt++;
			// cout<<cnt<<'\n';
			ans = ans * cnt % MOD;
			// cout<<ans<<"***\n";
		}
		printf("%lld\n",ans);
	}
	return 0;
}

J-一群小青蛙呱蹦呱蹦呱

题意:
在这里插入图片描述
思路:
根据题目的描述可以看到最后没有被吃掉的数一定具有两个及以上素因子的数,且我们还知道一堆数的lcm就等于这些数中各个不同素因子的最高次幂的积。
这样的话,我们的目标就变成了找到每个素因子在小于等于n的前提下,最多次幂是多少,最后答案就是他们的乘积。
由于至少具有两个不同的素因子这个限制,且保证幂次尽可能的多,那么对于除2以外的素因子我们最好的方法肯定是给他们先配一个2,再去计算幂次。同理对于2去计算幂次的时候呢,我们就给它先配一个3就好了。

出现素数的时候,要多去考虑素因子的性质来解题。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int MAXN = 1e8 + 7;
const int N = 1e7 + 7;
const int MOD = 1e9 + 7;
int prime[N],cnt,vis[MAXN];
ll q_pow(ll a,ll b){
	ll res = 1;
	while(b){
		if(b & 1) res = res * a % MOD;
		a = a * a % MOD;
		b >>= 1;
	}
	return res % MOD;
}
void Prime(){
	for(int i = 2;i < MAXN;i ++) {
		if(!vis[i]) prime[++cnt] = i;
		for(int j = 1;j <= cnt && prime[j] * i < MAXN;j ++){
			vis[prime[j]*i] = 1;
			if(i % prime[j] == 0) break;
		}
	}
}
int main(){
	Prime();
	int n;
	scanf("%d",&n);
	if(n <= 5) {
		printf("empty\n"); return 0;
	}
	ll ans = 1;
	ll t = 3,tot = 0;
	while(t <= n) t *= 2,tot++;
	ans = ans * q_pow(2,tot-1) % MOD;
	//变形成统计每个质因子 在小于等于n的条件下 最高次幂为多少
	for(int i = 2;i <= cnt && 2 * prime[i] <= n;i ++) {
		tot = 0,t = 2;
		while(t <= n) t *= prime[i],tot++;
		ans = ans * q_pow(prime[i],tot-1) % MOD;
	}
	printf("%lld\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值