牛客周赛 Round83 ABCDEF 伪装成博弈的贪心专场

A 小紫的均势博弈


int n;
void solve()
{
	cin >> n;
	if (n & 1) cout << "kou" << endl;
	else cout << "yukari" << endl;
}


B 小紫的劣势博弈

贪心题目




const int N = 1e5 + 10;


int n;

LL a[N];
void solve()
{
	cin >> n;
	for (int i = 1;i <= n;i ++) cin >> a[i];
	sort(a + 1,a + 1 + n);
	
	LL ans = 0;
	for (int i = 1;i <= n;i ++)
	{
		if (i & 1) ans += a[i];
		else ans -= a[i];
	}
	
	cout << ans << endl;
}




C 小紫的01串操作

计数是否连续的1或0的段的数量,是否小于2



const int N = 1e5 + 10;


string s;
void solve()
{
	cin >> s;
	int c0 = 0,c1 = 0;
	char pre = s[0];
	
	if (s[0] == '0') c0 ++;
	else c1 ++;		
	
	for (int i = 1;i < s.size();i ++)
	{
		if (s[i] != pre) 
		{
			if (s[i] == '1') c1 ++;
			else c0++;
			pre = s[i];
		}
	}
	
	// cout << c0 << " " << c1 << endl;
	if (c1 <= 2 || c0 <= 2) cout << "Yes" << endl;
	else cout << "No" << endl;
}




D 小紫的优势博弈

这个题看看牛客官方的视频讲解,这个题写的很优雅,思路巧妙且代码优雅


const int N = 2e5 + 10;

int n;
string s;

void solve()
{
	cin >> n >> s;
	s = " " + s;
	LL c0 = 0,c1 = 0;
	LL ans = 0;
	

	map<LL,LL> mp;
	mp[0] = 1;
	
	//01]01011
	for (int i = n;i > 1;i --)
	{
		c0 ^= (s[i] == '0');
		c1 ^= (s[i] == '1');
		LL t = (c0 << 1) | c1;
		ans += mp[t];
		mp[t] = 1;
	}
	
	cout << fixed << setprecision(10) << 1.0 * ans / n << endl;
}


E 小紫的线段染色

如果有线段重叠次数>=3(巧妙利用离散化+差分求重叠次数),无论怎么染色都不会满足要求

剩下的求染色的线段的编号就是贪心


typedef pair<LL,LL> PLL;


const int N = 1e5 + 10;

int n;
struct Node
{
	int l,r,id;
	bool operator< (const Node& t) const
	{
		if (l == t.l) return r < t.r;
		return l < t.l;
	}
}a[N];

int ans[N];

void solve()
{
	cin >> n;
	
	vector<PLL> v;
	for (int i = 1;i <= n;i ++)
	{
		cin >> a[i].l >> a[i].r;
		a[i].id = i;
		v.push_back({a[i].l,1});//差分
		v.push_back({a[i].r + 1,-1});//差分
	}
	
	sort(v.begin(),v.end());
	
	
	LL sum = 0;//利用前缀和思想,判断是否有重叠>=3次
	for (auto &[x,y] : v)
	{
		sum += y;
		if (sum >= 3) 
		{
			cout << -1 << endl;
			return;
		}
	}
	
	
	sort(a + 1,a + 1 + n);

	int r = -1,id = 0;
		
	for (int i = 1;i <= n;i ++)
	{
		if (a[i].l <= r) ans[a[i].id] = (ans[id] ^ 1);
		if (a[i].r > r) r = a[i].r,id = a[i].id;
	}
	

	vector<int> ans1;
	
	for (int i = 1;i <= n;i ++)
		if (ans[i])
			ans1.push_back(i);

	
	if (!ans1.size()) ans1.push_back(1);//全是分开的,都没有交集。类似[] [] [] 
	
	cout << ans1.size() << endl;
	for (auto &t : ans1) cout << t << " ";
}



F 小紫的树上染色 最大值最小

贪心的想法:从叶节点开始自底往上贪心,记录i节点及其子树的数量sz,如果>二分的联通块的大小,就cnt++(表示染了一个紫色块,将这些红色块分割开,sz置为0),如果最后的sz<=k,说明二分的这个连通块偏大,需要缩小,反之就扩大

自底往上的树形dfs

可以参考一下画的(丑)



const int N = 1e5 + 10;


int n,k;
vector<int> g[N];
int cnt;

int dfs(int x,int pre,int size)
{
	int sz = 1;//加上当前的大小:1
	for (auto &y : g[x])
	{
		if (y == pre) continue;
		sz += dfs(y,x,size);
	}
	
	if (sz > size) //超过了红色连通块大小限制,染色成紫色
	{
		cnt++;
		return 0;
	}
	else return sz;//没超过就继续累加
}

bool check(int x)
{
	cnt = 0;
	dfs(1,-1,x);
	return cnt <= k;
}

void solve()
{
	cin >> n >> k;
	for (int i = 1;i < n;i ++)
	{
		int x,y;cin >> x >> y;
		g[x].push_back(y);
		g[y].push_back(x);
	}	
	
    //二分红色联通块的最大值
	int l = 0,r = n + 1;
	while(l + 1 != r)
	{
		int mid = l + r >> 1;
		if (check(mid)) r = mid;//最大值最小
		else l = mid;
	}
	
	if (check(l)) cout << l << endl;//如果满足就 取小
	else cout << r << endl;//不满足再取大
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值