NOIP2011提高组题解

(D1T1)铺地毯((OK))

(D1T2)选择客栈((OK))

(D1T3)(Mayan)游戏

(D2T1)计算系数((OK))

(D2T2)聪明的质监员((OK))

(D2T3)观光公交((OK))

(Mayan)游戏先咕一下.

(D1T1)就直接倒序判断该点是否被地毯覆盖即可.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=10005;
struct dt{int x1,y1,x2,y2;}a[N];
int main(){
	int n=read();
	for(int i=1;i<=n;++i){
		a[i].x1=read();a[i].y1=read();
		a[i].x2=a[i].x1+read();a[i].y2=a[i].y1+read();
	}
	int x=read(),y=read();
	for(int i=n;i>=1;--i){
		if(x>=a[i].x1&&x<=a[i].x2&&y>=a[i].y1&&y<=a[i].y2){
			printf("%d
",i);return 0;
		}
	}
	puts("-1");
    return 0;
}

(D1T2)做过好多次了,最简单的是预处理前缀和,然后就可以(O(nk))计算了.还有一种更优秀的做法可以达到(O(n)),一边扫描一边维护那个前缀和,每次的(i)计算以该点为右端点的贡献即可.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=2e5+5;
int col[N],val[N],sum[51];
int main(){
	int n=read(),k=read(),p=read();
	for(int i=1;i<=n;++i)col[i]=read(),val[i]=read();
	int ans=0,last=0;
	for(int i=1;i<=n;++i){
		if(val[i]<=p){
			for(int j=last+1;j<i;++j)++sum[col[j]];
			last=i;ans+=sum[col[i]];++sum[col[i]];
		}
		else ans+=sum[col[i]];
	}
	printf("%d
",ans);
    return 0;
}

(D1T3)咕咕咕.

(D2T1)额,如果没有(a,b)就是多项式,那么系数就是组合数,然后现在有(a,b),其实就是乘上(a^n,b^m)即可.组合数可以递推也可以直接处理好阶乘及其逆元之后(O(1))计算.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int mod=10007;
int f[1005][1005];
inline ll ksm(int a,int b){
	ll cnt=1;
	while(b){
		if(b&1)cnt=(1ll*cnt*a)%mod;
		a=(1ll*a*a)%mod;
		b>>=1;
	}
	return cnt;
}
int main(){
	int a=read(),b=read(),k=read(),n=read(),m=read();
	for(int i=0;i<=k;++i){
		f[i][0]=1;
		for(int j=1;j<=i;++j)f[i][j]=(f[i-1][j]+f[i-1][j-1])%mod;
	}
	printf("%lld
",1ll*f[k][k-n]*ksm(a,n)%mod*ksm(b,m)%mod);
    return 0;
}

(D2T2)刚开始一看到这题:显然三分啊,因为(abs(S-T))是一个关于(W)的开口向上的二次函数,然后专门去速成了一波三分,结果只有(95)分,不想管了.放个代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline ll read(){
    ll x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=2e5+5;
int n,m;ll S,sum1[N],sum2[N];
int w[N],v[N],l[N],r[N];
vector<int>g[N];
inline ll calc(int W){
	ll ans=0;
	for(int i=1;i<=n;++i){
		sum1[i]=sum1[i-1];sum2[i]=sum2[i-1];
		if(w[i]>=W){
			++sum1[i];sum2[i]+=v[i];
		}
		for(int j=0;j<(int)g[i].size();++j){
			int l=g[i][j],r=i;
			ans+=1ll*(sum1[r]-sum1[l-1])*(sum2[r]-sum2[l-1]);
		}
	}
	return abs(S-ans);
}
int main(){
	n=read();m=read();S=read();
	for(int i=1;i<=n;++i)w[i]=read(),v[i]=read();
	for(int i=1;i<=m;++i)l[i]=read(),r[i]=read(),g[r[i]].push_back(l[i]);
	int l=0,r=1e6,mid1,mid2;
	while(l<r){
		mid1=(l+r)>>1;
		mid2=(mid1+r)>>1;
		ll f1=calc(mid1),f2=calc(mid2);
		if(f1>f2)l=mid1;
		if(f1<f2)r=mid2;
		if(f1==f2){
			if(mid1==mid2){
				printf("%lld
",f1);
				return 0;
			}
			r=mid2;
		}
	}
    return 0;
}

然后因为(S)是个定值,所以我们先不要考虑,那么(T)是一个关于(W)的单增函数,就可以二分答案了.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline ll read(){
    ll x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=2e5+5;
int n,m;ll S,sum1[N],sum2[N];
int w[N],v[N],l[N],r[N];
vector<int>g[N];
inline ll calc(int W){
	ll ans=0;
	for(int i=1;i<=n;++i){
		sum1[i]=sum1[i-1];sum2[i]=sum2[i-1];
		if(w[i]>=W){
			++sum1[i];sum2[i]+=v[i];
		}
		for(int j=0;j<(int)g[i].size();++j){
			int l=g[i][j],r=i;
			ans+=1ll*(sum1[r]-sum1[l-1])*(sum2[r]-sum2[l-1]);
		}
	}
	return ans;
}
int main(){
	n=read();m=read();S=read();
	for(int i=1;i<=n;++i)w[i]=read(),v[i]=read();
	for(int i=1;i<=m;++i)l[i]=read(),r[i]=read(),g[r[i]].push_back(l[i]);
	int l=0,r=1e6,mid;ll ans;
	while(l<=r){
		mid=(l+r)>>1;
		ll T=calc(mid);
		if(T<=S){
			r=mid-1;
			if(ans>S-T)ans=S-T;
		}
		else{
			l=mid+1;
			if(ans>T-S)ans=T-S;
		}
	}
	printf("%lld
",ans);
    return 0;
}

(D2T3)这道题好像从去年开始就一直被安利,然后一直不会做,当然现在还是不会自己做...

预处理出不使用加速器的情况下,公交车到达每个车站的时间(arrive[i]).

然后考虑用一次加速器之后能影响到多少车站的乘客?假设当前在车站(i)到车站(i+1)之间使用一次加速器,然后(i)~(j)这些车站的(arrive)都可以减(1),其中(j)满足(arrive[j-1]>late[j-1],arrive[j]<=late[j]).这个可以(O(n))找到.

然后这样操作(k)次即可.时间复杂度就是(O(nk)).

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=1005;
const int M=10005;
int d[N],off[N],late[N],arrive[N];
struct ck{int tim,st,ed;}a[M];
int main(){
	int n=read(),m=read(),k=read();
	for(int i=1;i<n;++i)d[i]=read();
	for(int i=1;i<=m;++i){
		a[i].tim=read();a[i].st=read();a[i].ed=read();
		late[a[i].st]=max(late[a[i].st],a[i].tim);++off[a[i].ed];
	}
	for(int i=2;i<=n;++i)arrive[i]=max(arrive[i-1],late[i-1])+d[i-1];
	while(k--){
		int max_val=0,pos=0;
		for(int i=1;i<n;++i){
			if(!d[i])continue;//距离不能减到0
			int max_cnt=0,j=i+1;
			for(;j<=n;++j){
				max_cnt+=off[j];
				if(arrive[j]<=late[j])break;
			}
			if(max_cnt>max_val){
				max_val=max_cnt;
				pos=i;
			}
			i=j-1;//没有这一句虽然能过本题,但是时间复杂度理论上达到了kn^2,只能说NOIP数据水
		}
		--d[pos];
		for(int j=pos+1;j<=n;++j){
			--arrive[j];
			if(arrive[j]<late[j])break;
		}	
	}
	int ans=0;
	for(int i=1;i<=m;++i)ans+=arrive[a[i].ed]-a[i].tim;	
	printf("%d
",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/PPXppx/p/11798151.html