Educational Codeforces Round 86 (Rated for Div. 2)

A 当b>2*a时每次只减一个数会花费更少,否则先同减到其中一个数为0再计算会更少。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010;
int t,n,m;
ll x,y,a,b;
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>x>>y>>a>>b;
		ll ans=0;
		ans+=min(x,y)*b;
		ans+=(max(x,y)-min(x,y))*a;
		cout<<min(ans,(x+y)*a)<<endl;
	}
	return 0;
}

还有一种思路是:关于同减次数x的总花费是个单谷函数,所以可以三分。

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
ll t,a,b,x,y;
ll check(ll c)
{
	return c*b+abs(x-c)*a+abs(y-c)*a;
}
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>x>>y>>a>>b;
		ll l=0,r=max(x,y);
		while(l<r)
		{
			int mid1=l+(r-l)/3;
			int mid2=r-(r-l)/3;
			if(check(mid1)<check(mid2)) r=mid2-1;
			else l=mid1+1;
		}
		cout<<check(l)<<endl;
	}
}

B 如果t中只有一种字符那么它的周期k就是1已经最小了直接输出,否则周期不可能为1,但是周期为2是一定构造出来的,01交替即可,所以将相邻的两个相同字符中间加一个其他字符就可以了。(直接输出n个01也可以)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1010;
int t,n,m;
string s,st;
int main(){
	cin>>t;
	while(t--){
		cin>>s;
		bool flag=1;
		for(int i=0;i<s.size()-1;i++)
			if(s[i]!=s[i+1])
			{
				flag=0;
				break;
			 } 
		if(!flag) 
		{
			for(int i=0;i<s.size();i++)
				if(i<s.size()-1)
				{
					st.push_back(s[i]);
					if(s[i]==s[i+1])
					{
						if(s[i]=='1') st.push_back('0');
						else st.push_back('1');
					}
				}
				else st.push_back(s[i]);
			cout<<st<<endl;
			st.clear();//记得清空
		}
		else cout<<s<<endl; 
	}
	return 0;
}

C 统计再区间[li,ri]之间x的个数,x满足的条件是( (x%b)%a) != ( (x%a)%a)。
可以打表找规律:令Max=max(a,b),Lcm=lcm(a,b) , 满足条件的x都有一个共性
x%Lcm在区间 [Max,Lcm)内。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1010;
int t,n,m,a,b;
string s,st;
ll get(ll l,int x,int y)//统计1到l满足条件的个数
{
	return l/y*(y-x)+max(0ll,l%y-x+1);
}
int main(){
	cin>>t;
	while(t--){
		cin>>a>>b>>n;
		int x=max(a,b),y=a*b/__gcd(a,b);
		while(n--)
		{
			ll l,r;
			cin>>l>>r;
			cout<<get(r,x,y)-get(l-1,x,y)<<endl;
		}
	}
	return 0;
}

D 又是一道阅读理解。已知一个测试样例有n个数组,每个数组的长度不超过k,第i数组的长度是mi,让你把这一个测试样例分成多个,每个样例中长度大于等于i的数组数量不超过ci。
因为ci统计的是大于等于,所以先放长度最大的数组,这样放的时候已经放过的数组长度一定大于等于当前数组,所以当放第i个数组时如果样例中的数组数量a大于等于c[m[i]]时就不能放到这个样例了,因为放进去后大于等于m[i]的数量就是a+1一定大于等于c[m[i]]了,不满足题意。
还要使测试样例尽可能的少,所以就要让每个样例里面尽可能多,但是会有一个最大限制c[1],因为所有数组长度一定大于等于1,所以每个样例最多有c[1]个数组。
每个样例尽可能得多放,所以遍历ans个样例找第一个满足(数组数量小于c[m[i]])的样例,将m[i]放进去,如果找不到就再开一个样例。暴力去找的话肯定会超时,我们每次都放到第一个满足的样例,所以数组数量是单减的,在单减序列中找第一个小于某个数的位置可以用二分(可以自己写,也可以调用库函数),num[i]统计第i个样例中数组数量的负值,找第一个大于 -c[m[i]]的位置就可以了,如果超出ans,就再加一个样例。
关于单减简单证明一下,不是太严谨:

假设可以构造出来这样的单增样例:
a b
c d e(具体是几无所谓)
因为e可以放到第二行,那么c[e]一定大于等于3,就是说e可以放到数量小于3的行,第一行也是可以的,那么这个例子就与 我们希望的将每个数放到第一个满足的样例相矛盾了。所以就是单减的了。

看了一下大佬的思路,首先确定最少需要多少个样例,然后依次将每个数放进样例就可以了,思路很简单,代码更简单,tql

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010;
int t,n,k,a,b;
int c[N],m[N];
vector<int> ve[N];
int num[N];
int ans=1;
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",m+i);
	for(int i=1;i<=k;i++) scanf("%d",c+i);
	sort(m+1,m+n+1);
	int l=1;//l表示还能放数组的最小样例
	for(int i=n;i;i--)//倒序枚举先放最大的
	{
		int x=upper_bound(num+l,num+ans+2,-c[m[i]])-num;//num[ans+1]=0,可以直接二分
		if(x<=ans)
		{
			ve[x].push_back(m[i]);
			num[x]--;
			if(ve[x].size()==c[1]) l=x+1;//最多可以放c[1]个
		}
		else ve[++ans].push_back(m[i]),num[x]--;
	}
	printf("%d
",ans);
	for(int i=1;i<=ans;i++)
	{
		printf("%d ",ve[i].size());
		for(int j=0;j<ve[i].size();j++)
			printf("%d ",ve[i][j]);
		puts(""); 
	}
	return 0;
}
原文地址:https://www.cnblogs.com/neflibata/p/12871746.html