A.贪心:https://codeforces.com/contest/1996/problem/A
AC代码:
#include<bits/stdc++.h>
using namespace std;
int t,n;
int main(){
cin>>t;
while(t--){
cin>>n;
int k=n/4;
if(4*k==n) cout<<k<<endl;
else{
cout<<k+(n-4*k)/2<<endl;
}
}
}
B.枚举:https://codeforces.com/contest/1996/problem/B
AC代码:
#include<bits/stdc++.h>
using namespace std;
int t;
int a[1010][1010],n,k;
int main(){
cin>>t;
while(t--){
cin>>n>>k;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
char c;
cin>>c;
a[i][j]=(int)(c-'0');
}
}
for(int i=1;i<=n;i+=k){
for(int j=1;j<=n;j+=k){
cout<<a[i][j];
}
cout<<endl;
}
}
}
C.前缀和:https://codeforces.com/contest/1996/problem/C
问题其实就是:从两个串中截取同一区间,问有多少个字母不一样。
令为前i个字母中有多少个j的字母,假设求的区间是
.
答案就是
AC代码:
#include<bits/stdc++.h>
using namespace std;
int t;
char a[200010],b[200010];
int cun[30];
int sum1[200010][28];
int sum2[200010][28];
int n,q;
void init(){
for(int i=1;i<=n;i++){
for(int j=0;j<=26;j++){
if(a[i]==(char)('a'+j)) sum1[i][j]=sum1[i-1][j]+1;
else sum1[i][j]=sum1[i-1][j];
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<=26;j++){
if(b[i]==(char)('a'+j)) sum2[i][j]=sum2[i-1][j]+1;
else sum2[i][j]=sum2[i-1][j];
}
}
}
int main(){
cin>>t;
while(t--){
cin>>n>>q;
for(int i=1;i<=n;i++){
char kk;
cin>>kk;
a[i]=kk;
}
for(int i=1;i<=n;i++){
char kk;
cin>>kk;
b[i]=kk;
}
init();
while(q--){
int l,r;
scanf("%d%d",&l,&r);
int ans=0;
for(int j=0;j<=26;j++){
ans+=abs(sum1[r][j]-sum1[l-1][j]-(sum2[r][j]-sum2[l-1][j]));
}
printf("%d\n",ans/2);
}
}
}
D.枚举:https://codeforces.com/contest/1996/problem/D
考虑到轮换性,不妨令
因此:,于是先枚举 a
于是我们再枚举b,这样c的可行解的范围也出来了:
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t,n,x;
ll cnt;
void solve(){
for(int a=1;a*a*3<=n;a++){
for(int b=a;b*b<=n;b++){
if(a+b>=x) break;
if(a*b+a+b>n) break;
int fk=(n-a*b)/(a+b);
int ck=min(fk,x-a-b);
if(ck<b) continue;
//计算贡献
if(a==b){
cnt++;//a=b=c
cnt+=(ck-b)*3;
}
else{
cnt+=3;
cnt+=(ck-b)*6;
}
}
}
}
int main(){
cin>>t;
while(t--){
cin>>n>>x;
cnt=0;
solve();
cout<<cnt<<endl;
}
}
E递推:https://codeforces.com/contest/1996/problem/E
我们把0当成-1,1当成1,合法的区间也就是其值和为0,于是我们先处理出来前缀和sum
首先,我们固定y,看看他可以带来的贡献:
我们不妨让:.这里的j是相比其他距离y最近的
显然当在区间内x只可以落在
的位置上。
此时l可以落在的任何位置,而r可以落在
的任意地方。
于是对答案贡献.
至于x落在上的贡献,就和求j时的贡献一样(但是要*(j+2)),于是问题就是一个递推的过程。
我们记为y落在j时的贡献(算贡献只考虑左边,毕竟现在右边不一样了),于是
我们提前一步维护mp,这样每次跟新mp只要+(j+2)即可,不然还要维护一个数组存j的位置。
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
string s;
int t;
ll res=0;
ll mod=1e9+7;
int main(){
cin>>t;
while(t--){
map<ll,ll> mp;
res=0;
cin>>s;
ll cur=0;
mp[cur]=1;
int n=s.size();
for(int i=0;i<=n-1;i++){
cur+=(s[i]=='1'?1:-1);
res=(res+(n-i)*mp[cur])%mod;
mp[cur]=(mp[cur]+i+2)%mod;
}
cout<<res<<endl;
}
}
F二分+优先队列:https://codeforces.com/contest/1996/problem/F
假如k不大,显然我们直接优先队列每次取a中最大的即可(k次里不取那不白白浪费了)。
但是这里的k是1e9的级别就不可以这么干了。
事实上,这个a数组的元素中的最大值一定是不严格递减的,最后一定会小于某一个值。
于是我们令表示所有值刚好小于等于x时用的最少次数(
的实现就是枚举加和每一个
),他显然是单调的,我们得到
的最小的x。
此时我们让k减掉x,可以发现此时的k一定小于等于n(假如大于n,我们对每一个a[i]再减去他们的b[i],这样就可以得到一个更小的x),于是我们就可以快乐地直接求了。
对于前面那部分对答案的贡献,因为每一个a[i]都是减到<=x就停止,我们用等差数列公式即可。
具体可以见下面的AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll t,n,k,a[200010],b[200010];
bool check(ll x){
ll cnt=0;
for(int i=1;i<=n;i++){
if(a[i]<=x) continue;
if((a[i]-x)%b[i]==0) cnt+=(a[i]-x)/b[i];
else cnt+=(a[i]-x)/b[i]+1;
}
if(cnt>k) return 0;
return 1;
}
int main(){
cin>>t;
while(t--){
cin>>n>>k;
ll ans=0;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
ll l=0,r=1e9;
while(l<r){
int mid=(l+r)/2;
if(check(mid)==1) r=mid;
else l=mid+1;
}
ll x=l;
//特判x==0
//计算贡献
for(int i=1;i<=n;i++){
if(a[i]<=x) continue;
int k1;
if((a[i]-x)%b[i]==0){
k1=(a[i]-x)/b[i];
ans+=(k1)*(a[i]+a[i]-b[i]*(k1-1))/2;
}
else{
k1=(a[i]-x)/b[i]+1;
ans+=(k1)*(a[i]+a[i]-b[i]*(k1-1))/2;
}
ll hh=0;
a[i]=max(hh,a[i]-b[i]*k1);
k-=k1;
}
if(x==0){
cout<<ans<<endl;
continue;
}
priority_queue<pair<int,int> > q;
for(int i=1;i<=n;i++) q.push({a[i],i});
for(int i=1;i<=k;i++){
pair<int,int> ck=q.top();
q.pop();
ans+=ck.first;
q.push({ck.first-b[ck.second],ck.second});
}
cout<<ans<<endl;
}
}