25-暑期-来追梦noip-卷5 总结
开题顺序:B-A-C(D 没看)
时间分配:A 40min B 1h C 20min
A
简单二分,直接二分分的段数 \(x\),然后每个种类的贡献就是 \(\min(a_i,x)\),所以 check 就是把所有种类的贡献加起来然后判断是否不小于 \(x \times k\) 即可。
实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int n,k;
int a[N];
bool check(int x){
int sum=0;
for(int i=1;i<=n;i++)
sum+=min(a[i],x);
return sum>=x*k;
}
signed main(){
//freopen("T1.in","r",stdin);
//freopen("T1.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>k;
int s=0;
for(int i=1;i<=n;i++)
cin>>a[i],s+=a[i];
int l=-1,r=s/k+1;
while(l+1<r){
int mid=(l+r)>>1;
if(check(mid))
l=mid;
else
r=mid;
}
cout<<l;
return 0;
}
总结:
- 注意单调性的挖掘。
B
注意到,操作区间更大不会更劣,因为能覆盖更小的差。
所以二分一下长度,然后用线段树维护区间最值以处理边界即可。
实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int _,n,k;
int a[N],b[N],sum[N],tree[N<<2];
void pushup(int p){
tree[p]=max(tree[p<<1],tree[p<<1|1]);
}
void build(int p,int lt,int rt){
if(lt==rt){
tree[p]=a[lt];
return;
}
int mid=(lt+rt)>>1;
build(p<<1,lt,mid);
build(p<<1|1,mid+1,rt);
pushup(p);
}
int qry(int p,int lt,int rt,int ql,int qr){
if(lt>qr||rt<ql)
return (int)(-1e9);
if(ql<=lt&&rt<=qr)
return tree[p];
int mid=(lt+rt)>>1;
return max(qry(p<<1,lt,mid,ql,qr),qry(p<<1|1,mid+1,rt,ql,qr));
}
bool check(int x){
for(int i=1;i+x-1<=n;i++){
int mx=qry(1,1,n,i,i+x-1);
int val=sum[n]-sum[i+x-1]+sum[i];
if(i+x<=n)
val=val-b[i+x]+abs(mx-a[i+x]);
if(i>1)
val=val-b[i]+abs(mx-a[i-1]);
if(val<=k)
return 1;
}
return 0;
}
void solve(){
cin>>n>>k;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=2;i<=n;i++)
b[i]=abs(a[i]-a[i-1]),sum[i]=sum[i-1]+b[i];
build(1,1,n);
if(sum[n]<=k){
cout<<"0\n";
return;
}
int lt=1,rt=n+1;
while(lt+1<rt){
int mid=(lt+rt)>>1;
if(check(mid))
rt=mid;
else
lt=mid;
}
cout<<rt<<'\n';
}
signed main(){
//freopen("T2.in","r",stdin);
//freopen("T2.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin>>_;
while(_--)
solve();
return 0;
}
C
如果不是勉强相等而是完全相等,那么直接树状数组维护哈希值即可。
现在是勉强相等,则直接二分找出最早的不相同位置,然后检查后面是否相同即可判断是否勉强相等。
实现
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
using namespace std;
const int N=1e5+5;
const ull BASE=13331;
int n,m;
ull tree[N];
string s;
ull P[N];
int lowbit(int x){
return x&-x;
}
void upd(int x,int y){
int sum=0;
for(;x<=n;sum+=lowbit(x),x+=lowbit(x))
tree[x]+=y*P[sum];
}
ull qry(int x){
int sum=0;
ull res=0;
for(;x;sum+=lowbit(x),x-=lowbit(x))
res+=tree[x]*P[sum];
return res;
}
ull qryr(int l,int r){
return qry(r)-qry(l-1)*P[r-l+1];
}
signed main(){
//freopen("T3.in","r",stdin);
//freopen("T3.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m>>s,s='#'+s;
P[0]=1;
for(int i=1;i<=n;i++)
P[i]=P[i-1]*BASE;
for(int i=1;i<=n;i++)
upd(i,s[i]-'a'+1);
while(m--){
int op,x,l1,r1,l2,r2;
char y;
cin>>op;
if(op==1){
cin>>x>>y;
upd(x,y-'a'+1-(s[x]-'a'+1));
s[x]=y;
}
else{
cin>>l1>>r1>>l2>>r2;
if(r1-l1+1!=r2-l2+1){
cout<<"NO\n"; continue;
}
int lt=1,rt=r1-l1+2;
while(lt<rt){
int mid=(lt+rt)>>1;
if(qryr(l1,l1+mid-1)==qryr(l2,l2+mid-1))
lt=mid+1;
else
rt=mid;
}
if(lt==r1-l1+2)
cout<<"YES\n";
else if(qryr(l1+lt,r1)!=qryr(l2+lt,r2))
cout<<"NO\n";
else
cout<<"YES\n";
}
}
return 0;
}
总结:
- 树状数组维护哈希值。
D
根号分治思想。
对于 \(y>10^6\),容易发现转换进制后的 \(x\) 最多 \(3\) 位,所以直接枚举 \(x\) 然后二分进制(显然进制越大得到的数越大),看是否可以得到原来的 \(y\)。
对于 \(y \le 10^6\),枚举 \(y\) 转换为 \(x\) 然后检查即可。就是暴力。
实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int get(int x,int y){
int res=0,tmp=1;
while(x){
if(x%y>=10)
return -1;
res+=(x%y)*tmp,x/=y,tmp*=10;
}
return res;
}
int check(int x,int y){
return x%10+(x/10%10)*y+(x/100)*y*y;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
int ans=10;
for(int i=m;i<=999;i++){
int lt=1,rt=(int)(1e18)+1;
if(i>=100)
rt=(int)(1e9)+1;
while(lt+1<rt){
int mid=(lt+rt)>>1;
if(check(i,mid)>=n)
rt=mid;
else
lt=mid;
}
if(check(i,rt)==n)
ans=max(ans,rt);
}
for(int i=(int)(1e6);i>=ans;i--){
if(get(n,i)>=m){
ans=i; break;
}
}
cout<<ans;
return 0;
}
总结:
- 数据范围较大时使用根号分治思想。
结语
成绩:20+100+10+10=140,没有挂分。
问题:性质挖掘不够深入(尤其是单调性)、根号分治思想运用不熟练,不会树状数组维护哈希值。
方案:同上总结。
以上。

浙公网安备 33010602011771号