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,没有挂分。

问题:性质挖掘不够深入(尤其是单调性)、根号分治思想运用不熟练,不会树状数组维护哈希值。

方案:同上总结。

以上。

posted @ 2025-08-18 22:36  _KidA  阅读(6)  评论(0)    收藏  举报