Codeforces Div2 #250

本文详细解析了五道算法竞赛题目,包括贪心算法、图论、动态规划、数据结构等核心内容,提供了高效的实现思路及代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

B-The Child and Set

这道题有一个非常暴力的做法就是直接求出所有的数的lowbit,也就是x&(-x),然后直接排个序,从大到小往下减就可以了。但是这个贪心并不是可推广的。在这里特殊的是,通过转化最终对于确定的k,2^k次方最终都只会有一个。考虑把最后的数字x写成二进制的形式,每个数都是一个只有一位是1的串。这样考虑每次都减高位的贪心就没有问题了。

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define pb push_back
#define F first
#define S second
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
int lowbit(int x) { return x&(-x); }
vector<pair<int,int> >v;
bool cmp(pair<int,int> &a, pair<int,int> &b) {
	return a.first>b.first;
}
vector<int> ans;
int main() {
	int s,l;
	scanf("%d%d",&s,&l);
	for(int i=1;i<=l;i++)
		v.push_back(make_pair(lowbit(i),i));	
	sort(v.begin(),v.end(),cmp);
	for(auto &x:v) {
		if(s>=x.first) s-=x.first,ans.push_back(x.second);
	}
	if(s!=0) puts("-1");
	else {
		printf("%d\n",ans.size());
		for(auto &x:ans) {
			printf("%d ",x);
		} 
	}
	return 0;
}

C-The Child and Toy

这个题目是说每次都取走一个点以及所有和它相连的边,然后取走的代价是相邻的所有点的权值之和。要求最小的代价。如果从另外一个方面想,考虑每条边的贡献:那么这条边的贡献一定是连接的两个点的其中一个点。那么我们只要每次都取那个权值最大的点,一定会使得每条边的贡献变成相对较小的那个点。所以只要贪心权值最大的点就可以了。

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define pb push_back
#define F first
#define S second
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
int lowbit(int x) { return x&(-x); }
vector<pair<int,int> >v;
bool cmp(pair<int,int> &a, pair<int,int> &b) {
	return a.first>b.first;
}
vector<int> ans;
int main() {
	int s,l;
	scanf("%d%d",&s,&l);
	for(int i=1;i<=l;i++)
		v.push_back(make_pair(lowbit(i),i));	
	sort(v.begin(),v.end(),cmp);
	for(auto &x:v) {
		if(s>=x.first) s-=x.first,ans.push_back(x.second);
	}
	if(s!=0) puts("-1");
	else {
		printf("%d\n",ans.size());
		for(auto &x:ans) {
			printf("%d ",x);
		} 
	}
	return 0;
}

D-The Child and Zoo

最后两点直接连通的时候要使得这两点之间的最小权值最大。考虑每条边对答案的贡献,边权是连接两点的点权的最小值。然后构造一个最大生成树就可以了,每次连接两个不同的连通块的时候,对答案的贡献就是边权和两边连通块大小的乘积。这个思想和B题有点类似。都是点的贡献不好求的时候考虑转化为边的贡献。

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define pb push_back
#define F first
#define S second
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
const int N=1e5+50;
struct edge{
	double w;
	int u,v;
}e[N<<2];
bool cmp(edge x,edge y) {
	return x.w>y.w;
}
double w[N];
int fa[N];
ll rk[N];
int find(int x)  {	return fa[x]==x?fa[x]:fa[x]=find(fa[x]); }
int main() {
	ll n,m;
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++) {
		scanf("%lf",&w[i]);
		fa[i]=i,rk[i]=1;	
	} 
	for(int i=1;i<=m;i++) {
		int u,v;
		scanf("%d%d",&u,&v);
		e[i].u=u,e[i].v=v;
		e[i].w=min(w[u],w[v]);
	}
	sort(e+1,e+m+1,cmp); 
	double sum=0.0;
	for(int i=1;i<=m;i++) {
		int U=find(e[i].u);
		int V=find(e[i].v);
		if(U!=V) {
			sum+=e[i].w*(double)rk[U]*(double)rk[V]/(double)(n*(n-1));
			rk[U]+=rk[V],fa[V]=U;
		}	
	}
	printf("%lf\n",sum*2.0);
	return 0;
}

E-The Child and Polygon

题目已经明确说了所有的点都是按照逆时针或者顺时针给出的,这里为了方便统一处理把点的顺序都转化为逆时针。先叉积一遍求一下面积,如果面积为负数说明是按照逆时针的顺序给出的。然后这里考虑一个区间dp,dp[i][j]表示我从i点到j点连成的这个多边形可以被分割为多少个三角形。
转移就是dp[i][j]=∑dp[i][k]∗dp[k][j],i+1≤k≤j−1dp[i][j]=∑dp[i][k]*dp[k][j],i+1\leq k \leq j-1dp[i][j]=dp[i][k]dp[k][j]i+1kj1,但是这里k必须要合法地分割四边形。ij⃗∗ik⃗&lt;0\vec {ij}* \vec {ik}&lt;0ijik<0的时候情况才是合法的。否则这个四边形会被分割为两个本来不存在的多边形。

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define pb push_back
#define F first
#define S second
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
const int N=205;
struct node{
	ll x,y;
	node() {}
	node(ll x,ll y):x(x),y(y) {}
	node operator -(const node &rhs) {
		return node(x-rhs.x,y-rhs.y);
	} 
}p[N];
ll det(node a,node b)  { return a.x*b.y-a.y*b.x; }
ll dp[N][N];
const ll mod=1e9+7;
int main() {
	int n;
	ll sum=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	 	scanf("%lld%lld",&p[i].x,&p[i].y);
	for(int i=2;i<=n;i++)
		sum+=det(p[i],p[i+1]);
	sum+=det(p[n],p[1]);
	if(sum>0) reverse(p+1,p+n+1);
	for(int i=1;i<n;i++) dp[i][i+1]=1;
	for(int len=3;len<=n;len++) {
		for(int i=1;i+len-1<=n;i++) {
				int j=i+len-1;
				for(int k=i+1;k<=j-1;k++) {
					if(det(p[j]-p[i],p[k]-p[i])>0)
						dp[i][j]+=dp[i][k]*dp[k][j],dp[i][j]%=mod;
				}
			} 
		}
	printf("%lld\n",dp[1][n]);
	return 0;
}

F-The Child and Sequence

一道数据结构题目。要支持三种操作:1.区间求和 2.单点修改 3.区间取模。对于第一种和第二种都比较简单线段树上log的复杂度就可以完成了。但是对于第3种操作似乎略微有些复杂。因为我们都知道对于一个比模数要小的数取模显然并不会对这个数产生影响。我们可以维护这个线段树里面一个区间的最大值。如果这个区间的最大值已经小于模数,就没有必要再去修改它。否则我们就直接递归进去单点修改所有的数。这个最坏的情况好像是O(n),但是我们注意到两个事实:1.在不修改的情况下,一个数被取模最多log次。2.由于是单点修改,需要多次单点修改才能使得复杂度变坏。
所以这个取模只要暴力去做就可以了。

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
#define pb push_back
#define F first
#define S second
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
const int N=1e5+50;
int a[N];
ll sum[N<<2],Max[N<<2]; 
void update(int rt) {
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
	Max[rt]=max(Max[rt<<1],Max[rt<<1|1]); 
}
void build(int rt,int l,int r) {
	if(l==r) { sum[rt]=a[l],Max[rt]=a[l]; return; }
	int mid=(l+r)>>1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
	update(rt);
}
void modify(int rt,int l,int r,int k,int x) {
	if(l==r) { sum[rt]=x,Max[rt]=x;return; }
	int mid=(l+r)>>1;
	if(k<=mid) modify(rt<<1,l,mid,k,x);
	else modify(rt<<1|1,mid+1,r,k,x);
	update(rt);
}
void modify_mod(int rt,int l,int r,int L,int R,int mod) {
	if(L>r||R<l) return;
	if(Max[rt]<mod) return;
	if(l==r) { sum[rt]%=mod,Max[rt]%=mod; return;}
	int mid=(l+r)>>1;
	modify_mod(rt<<1,l,mid,L,R,mod);
	modify_mod(rt<<1|1,mid+1,r,L,R,mod);
	update(rt);
}
ll query(int rt,int l,int r,int L,int R) {
	if(R<l||L>r) return 0;
	if(L<=l&&r<=R) return sum[rt];
	int mid=(l+r)>>1;
	return query(rt<<1,l,mid,L,R)+query(rt<<1|1,mid+1,r,L,R);
}
int main() {
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]); 
	build(1,1,n);
	for(int i=1;i<=m;i++) {
		int opt;
		scanf("%d",&opt);
		if(opt==1) {
			int l,r;
			scanf("%d%d",&l,&r);
			printf("%lld\n",query(1,1,n,l,r));
		}
		if(opt==2) {
			int l,r,x;
			scanf("%d%d%d",&l,&r,&x);
			modify_mod(1,1,n,l,r,x);
		}
		if(opt==3) {
			int k,x;
			scanf("%d%d",&k,&x);
			modify(1,1,n,k,x);
		}
	} 
	return 0;
}

G-The Child and Binary Tree

这个题好像是个多项式啊…我可能还是需要先学习一下然后再做啊。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值