• 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;
    }
    
    
  • 相关阅读:
    寒假瞎搞系列(5)
    《亿级用户下的新浪微博平台架构》阅读笔记
    《蚂蚁金服11.11:支付宝和蚂蚁花呗的技术架构及实践》阅读笔记
    《游戏服务器的架构演进(完整版)》阅读笔记
    阅读架构漫谈(四)笔记
    阅读架构漫谈(三)笔记
    阅读架构漫谈(二)笔记
    以《淘宝网》为例,描绘质量属性的六个常见属性场景
    阅读架构漫谈(一)笔记
    假期周进度总结2
  • 原文地址:https://www.cnblogs.com/PPXppx/p/11798151.html
Copyright © 2020-2023  润新知