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\n",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;
}