当前位置: 首页 > article >正文

Educational Codeforces Round 171 (Rated for Div. 2) A~E

A - Perpendicular Segments

找对角线

void solve()
{
	cin>>n>>m>>k;
	int t=min(n,m);
	cout<<0<<" "<<0<<" "<<t<<" "<<t<<'\n';
	cout<<t<<" "<<0<<" "<<0<<" "<<t<<'\n';
	return ;
}

B - Black Cells

模拟

int q[N];
bool check(int x)
{
	if(n%2==0)
	{
		_rep(i,2,n)
		{
			if(q[i]-q[i-1]<=x)i++;
			else return false;
		}
		return true;
	}
	else 
	{
		if(n==1)return true;
		_rep(i,1,n)
		{
			bool bl=true;
			for(int j=1;j<=n-1;j+=2)
			{
				if(j==i){j--;continue;};
				if(j+1==i)
				{
					if(q[j+2]-q[j]>x)
					{
						bl=false;
						break;
					}
				}
				else 
				{
					if(q[j+1]-q[j]>x)
					{
						bl=false;
						break;
					}
				}
			}
			if(bl)return true;
		}
		return false;
	}
	return true;
}
void solve()
{
	cin>>n;
	_rep(i,1,n)cin>>q[i];
	int l=1,r=1e18;
	while(l<r)
	{
		int mid=l+r>>1;
		check(mid)?r=mid:l=mid+1;
	}
	cout<<l<<"\n";
	return ;
}

C - Action Figures

题意

给定一个长度为n的01序列,第i个点的价格是i,假设第i个点可以在i~n的任意时刻购买,并且如果一次性买了>=2个物品的话最贵的物品可以免费

思路

结合样例很快就可以发现

第i个点假如为0的话,那我不可避免的一定要花钱买i,为了使花费最少,也就是免费的物品的价格最高,那么我一定是尽可能找一个最右边的1来和这个0配对,然后使这个1对应的物品免费,用队列即可实现

所以从后往前遍历,遇到1就push,遇到0就看当前右边是否还有1(即queue的size是否>0),如果有就出队,然后出队的点就可以免费拿

注意的是,遍历完数组之后,假如队列的大小>=2说明我可以用数组中靠左边的1来免费拿靠右边的1,于是靠右边的1也免费

const int N=1e6+10,INF=4e18;
int n,m;
void solve()
{
	deque<int>q;
	cin>>n;
	string s;
	cin>>s;
	int res=(n+1)*n/2;
	_pre(i,(int)s.size()-1,0)
	{
		if(s[i]=='0')
		{
			if(q.size())res-=q.front(),q.pop_front();
		}
		else 
			q.pb(i+1);
	}
	while(q.size()>=2)
	{
		res-=q.front();
		q.pop_back();
		q.pop_front();
	}
	cout<<res<<'\n';
	return ;
}

D - Sums of Segments

题意

思路

可以发现根据这个图(假设n=6),可以把所有的前缀和分为六个块,每个块分别是6,5,4,3,2,1,第一个块的大小可以用前缀和的前缀和求出来,然后由第一个块可以推出第二个块(可以发现就是第一个元素*第一个块的行数),第二个块又可以推出第三个块,以此类推,那么我可以求出每个块的大小,那么L+1~R-1如果相差多个块,那么可以用块的前缀和O(1)求出,现在只需要求出L所在块(所在哪个块可以用二分找出)的下半部分,以及R所在块的上半部分即可,可以发现图中L所在块的下半部分,其实就是第一个块的4 5 6行减去第1个元素的值*3,也就是说这个下半部分也可以从第一块推出来(只不过要记录一下L实际上是在这个块中的第几个元素),R的上半部分计算同理,只需要推公式+利用一下前缀和以及前缀和的前缀和即可,要注意当L==R的时候,L的下半部分和R的上半部分相加其实是重复算了整个当前块的值,所以此时求得的答案减去这一块的值即可

#include<iostream>
#include<map>
#include<cmath>
#include<string>
#include<vector>
#include<cstring>
#include<algorithm>
#include<unordered_map>

#define pb push_back
#define fi first
#define se second
#define int long long
#define all(x) (x).begin(),(x).end()
#define _for(i, a) for(int i = 0; i < (a); ++i) 
#define _rep(i, a, b) for(int i = (a);i <= (b); ++i)
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define u1 (u<<1)
#define u2 (u<<1|1)
//#define endl '\n'
using namespace std;
typedef pair<int,int> PII;

const int INF=0x3f3f3f3f;
const int P=1e9+7;
const int N=1e6+20,M=2*N;
int n,m,k,q[N];
int qian[N],qqian[N],row[N];
bool check(int x,int a)
{
	int sum=n*(n+1)/2;
	sum-=(n-x+1)*(n-x)/2;
	return sum>=a;
}
int deal(int a)
{
	int l=1,r=n;
	while(l<r)
	{
		int mid=l+r>>1;
		check(mid,a)?r=mid:l=mid+1;
	}
	return l;
}
void init()
{
	_rep(i,1,n)
	{
		qqian[1]+=qian[i];
		row[i]=row[i-1]+qian[i];
	}
	int sub=n;
	_rep(i,2,n)
	{
		qqian[i]=qqian[i-1]-qqian[i-2]-q[i-1]*sub+qqian[i-1];
		sub--;
	}
}
int getkuai(int x,int id,int op)//块号,编号
{
	int sum=n*(n+1)/2;
	sum-=(n-x+2)*(n-x+1)/2;
	int l,r;
	if(op==1)
	{
		l=id-sum;//块内编号
		r=n-x+1;//块内编号
	}
	else
	{
		l=1;
		r=id-sum;
	}
	return row[r+x-1]-row[l+x-2]-(r-l+1)*qian[x-1];
}
void solve(){
	cin>>n;
	_rep(i,1,n)cin>>q[i],qian[i]=qian[i-1]+q[i];
	init();
	cin>>m;
	while(m--)
	{
		int l,r;
		cin>>l>>r;
		int sum=0;
		int a=deal(l),b=deal(r);
		if(a+1<=b-1)sum+=qqian[b-1]-qqian[a];
		sum+=getkuai(a,l,1);
		sum+=getkuai(b,r,2);
		if(a==b)sum-=qqian[a]-qqian[a-1];
		cout<<sum<<'\n';
	}
}
signed main(){
	IOS;
	int T=1;
//	cin>>T;
	_rep(i,1,T){
		solve();
	}
	return 0;
}

E - Best Subsequence

(个人感觉E<D)

题意

定义一个数组的'值'为数组长度减去数组所有元素或起来之后的二进制中1的个数,给定一个数组,问在这个数组中的所有子序列(包括自己)的最大'值'是多少

思路

根据样例可以发现一个规律

假设这个数组为{1,2,3},那么可以发现

以{1}为子序列,或起来之后的二进制中1的个数为1,数组长度为1

以{2}为子序列,或起来之后的二进制中1的个数为1,数组长度为1

以{3}为子序列,或起来之后的二进制中1的个数为2,数组长度为1('值'为-1)

以{1,2}为子序列,或起来之后的二进制中1的个数为2,数组长度为2

以{1,2,3}为子序列,或起来之后的二进制中1的个数为2,数组长度为3('值'为1,最优)

可以发现,当选一个数字作为子序列的一部分时候,这个数字如果贡献出来的1的个数<=1(假设为a),那么这个数字就可以使得数组长度+1,并且或起来之后的二进制中1的个数+a(a<=1)

如果以{3}为子序列,那么可以发现此时'值'为-1,比什么都不选的‘值‘0还要小,显然不会选3

但是如果以{1,2}为子序列,此时'值'为0,这个时候再选上3,可以发现这次选上这个3,贡献出来的1的个数=0,于是这个新子序列{1,2,3}的'值'就为1也就是3有贡献了

如何模拟这种3为负贡献的时候不选,然后3为正贡献的话就选(已经选定{1,2}之后再选3的情况)的这种场景呢

那就是让3占个'坑位',也就是让3随便占一个位置(前提是自己这一位二进制下为1才能占,而且不能有选择性的占而是随便占一个位置),这样随便占虽然说,这个占坑位的3贡献了一个二进制中1的个数和一个数组长度,那么'值'当然是不变的,但是这样占下去如果碰到下一个数字,它的每一位都被占用了,那么此时这个数字的贡献的1的个数就为1,那么'值'就可以+1

综上所述,可以让一个点随机占一个自己的某一位二进制位1下的位,然后接下来的点如果要占的点被前面的点占了,就让前面的点退而求其次找另一个自己二进制位1下的位的点(模拟这个随机占位的过程),这个过程也就相当于是二分图的匹配,最后的最大值即为n-最大匹配数

#include <map>
#include <set>
#include <queue>
#include <deque>
#include <cmath>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define fi first
#define se second
#define u1 (u<<1)
#define u2 (u<<1|1)
#define pb push_back
#define pp pop_back()
#define int long long
#define laile cout<<"laile"<<endl
#define lowbit(x) ((x)&(-x))
#define double long double
#define sf(x) scanf("%lld",&x)
#define sff(x,y) scanf("%lld %lld",&x,&y)
#define sd(x) scanf("%Lf",&x)
#define sdd(x,y) scanf("%Lf %Lf",&x,&y)
#define _for(i,n) for(int i=0;i<(n);++i)
#define _rep(i,a,b) for(int i=(a);i<=(b);++i)
#define _pre(i,a,b) for(int i=(a);i>=(b);--i)
#define all(x) (x).begin(), (x).end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
typedef unsigned long long ULL;
typedef pair<int,int>PII;
typedef pair<double,double>PDD;
const int N=210,M=6010,INF=4e18;
int n,m;
int e[M],ne[M],h[N];
bool st[N];
int idx,mac[N];
void add(int a,int b){e[idx]=b;ne[idx]=h[a];h[a]=idx++;}
bool match(int u)
{
	for(int i=h[u];i!=-1;i=ne[i])
	{
		int j=e[i];
		if(!st[j])
		{
			st[j]=true;
			if(!mac[j]||match(mac[j]))
			{
				mac[j]=u;
				return true;
			}
		}
	}
	return false;
}
void solve()
{
	cin>>n;
	memset(h,-1,sizeof h);
	memset(mac,0,sizeof mac);
	idx=0;
	_rep(i,1,n)
	{
		cin>>m;
		_rep(j,0,60)
		if(m>>j&1)
			add(i,j+n+1);
	}
	int cnt=0;
	_rep(i,1,n)
	{
		memset(st,false,sizeof(st));
		if(match(i))cnt++;
	}
	cout<<n-cnt<<'\n';
	return ;
}
signed main()
{
	IOS;
	int T=1;
    cin>>T;
	while(T--)
		solve();
	return 0;
}


http://www.kler.cn/a/371330.html

相关文章:

  • 【分布式知识】分布式对象存储组件-Minio
  • 关于InternVL2的模型训练二(如何训练目标定位模型)
  • 大语言模型数据处理方法(基于llama模型)
  • SpringMVC实战:构建高效表述层框架
  • 钉钉与金蝶云星空数据集成:提高企业付款申请单处理效率
  • ffmpeg拉流分段存储到文件-笔记
  • 【Linux】动静态库:构建强大软件生态的基石
  • 张量分析与连续介质力学
  • 【vue】07.自定义指令
  • GitHub Copilot将支持来自Anthropic、Google和OpenAI的模型
  • 双指针——对撞指针与左右指针
  • Twitter网页版怎么登录?详细步骤与常见问题解答
  • kotlin的this和it用法
  • ffmpeg视频滤镜:膨胀操作-dilation
  • 算法:常见位运算技巧总结
  • Dirichlet分布生成联邦学生non-iid数据
  • css实现背景色的斑马条效果
  • 如何用李萨如图形测正弦信号的频率?若不使用李萨如图形,如何用示波器测交流信号频率?
  • PHP内存马:不死马
  • 微信小程序如何实现地图轨迹回放?
  • 地球上的中国:世界地图概览
  • Go中的泛型
  • NFS服务器作业
  • Linux云计算 |【第五阶段】CLOUD-DAY1
  • 字母象形与hand的不同解构
  • 【机器学习】揭秘XGboost:高效梯度提升算法的实践与应用