7.10 NOIP 2011

NOIP 2011 DAY 1

T1
在这里插入图片描述
简单模拟

#include<bits/stdc++.h>
using namespace std;
int a[10010][10010];
int n,x,y;
bool flag;
int main(){
//	freopen("carpet.in","r",stdin);
//	freopen("carpet.out","w",stdout); 
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i][0]>>a[i][1]>>a[i][2]>>a[i][3];
        a[i][2]+=a[i][0];
        a[i][3]+=a[i][1];
    }
    cin>>x>>y;
    for(int i=n;i>=1;i--){
        if(a[i][0]<=x && a[i][1]<=y && a[i][2]>=x && a[i][3]>=y)
        {
            cout<<i;
            flag=1;
            break;
        }
    }
    if(!flag) cout<<-1;
}

T2
在这里插入图片描述
想了半天正解还是降不下来时间复杂度,最后直接打了个三重循环暴力,一层1客栈,二层2客栈,3层合理的位置,拿了60分,其实只需要小小的优化就可以去掉两重循环了。
60代码

#include<bits/stdc++.h>
using namespace std;
int flag[200010],c[200010],b[200010],sum[200010];
int n,m,x;
int ans;
int main(){
//	freopen("hotel.in","r",stdin);
//	freopen("hotel.out","w",stdout); 
	cin>>m>>n>>x;
	for(int i=1;i<=m;++i){
		scanf("%d%d",&b[i],&c[i]);
		sum[b[i]]++;
		if(c[i]<=x) flag[i]=1;
	}
	for(int i=1;i<=m;++i){
		if(flag[i]) {
			ans+=sum[b[i]]-1;
			sum[b[i]]--;
			continue;
		}
		for(int j=i+2;j<=m;++j){
			if(b[i]==b[j] && !flag[j]){
				for(int k=i+1;k<j;++k){
					if(c[k]<=x) {
					ans++;
					break; 
					}
				}
			}
		}
	}
	cout<<ans;
} 

只需要枚举第二个客栈,然后用第二个客栈反推出前面的方案数。就是,从1到n枚举,记录一个距离第二个客栈最近的咖啡厅价钱合理的客栈位置,用now记录。如果我发现枚举的时候在某一个客栈前面有一个价钱合理的咖啡厅,那么在这之前的任何一个同色客栈都是这个客栈可以选的。
100代码

#include<bits/stdc++.h>
using namespace std;
int cnt[10001],sum[10001],last[10001];
int n,m,x,a,b,now,ans;
int main(){
	cin>>n>>m>>x;
	for(int i=1;i<=n;i++){
		cin>>a>>b;
		if(b<=x){
			now=i;
		} 
		if(now>=last[a]){
			sum[a]=cnt[a];
		}
		last[a]=i;
		ans+=sum[a];
		cnt[a]++; 
	}
	cout<<ans;
}

DAY 2

T1
在这里插入图片描述
用f[i][j]表示x^i y^j的系数,可以得到转移f[i][j]=f[i-1][j]*a+f[i][j-1]*b,然后注意一下初始化。

#include<bits/stdc++.h>
using namespace std;
#define mo 10007
int n,m,a,b,k;
long long dp[10001][10001];
int main(){
//	freopen("factor.in","r",stdin);
//	freopen("factor.out","w",stdout);
	cin>>a>>b>>k>>n>>m;
	dp[0][0]=1;
	for(int i=0;i<=n;i++){
		for(int j=0;j<=m;j++){
			if(!j && !i ) continue;
			if(i){
				dp[i][j]+=dp[i-1][j]*a%mo;
			}
			if(j){
				dp[i][j]+=dp[i][j-1]*b%mo;
			}
			dp[i][j]%=mo;
		}
	}
	cout<<dp[n][m];
} 

T2
在这里插入图片描述
这道题其实很明显的二分,显然我们无法直接算出W的值,只能枚举W,比较Y与S的大小,然后缩减枚举范围。(然后打了个超级暴力还打错了)另一个就是前缀和。因为区间很多,而且区间范围也很大,所以我们先预处理出所有sum[i],然后要求l[i]到r[i]的区间,只需求sum[r[i]]-sum[l[i]-1]。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=200100;
LL ans=1e15;
LL sw[N],sv[N],w[N],v[N];
int l[N],r[N];
int n,m;
LL S;
LL f(LL a,LL b){
    return a>b?a-b:b-a;
}
bool check(LL mid){
    for (int i=1;i<=n;i++){
        sw[i]=sw[i-1]+(w[i]>=mid);
        sv[i]=sv[i-1]+(w[i]>=mid)*v[i];
    }
    LL W=0;
    for (int i=1;i<=m;i++){
        W+=(sw[r[i]]-sw[l[i]-1])*(sv[r[i]]-sv[l[i]-1]);
    }
    ans=min(ans,f(W,S));
    return W<=S;
}
int main(){
    scanf("%d%d%lld",&n,&m,&S);
    LL Left=0,Right=0;
    for (int i=1;i<=n;i++){
        scanf("%lld%lld",&w[i],&v[i]);
        Right=max(Right,w[i]);
    }
    Right++;
    for (int i=1;i<=m;i++){
        scanf("%d%d",&l[i],&r[i]);
    }
    while (Left+1<Right){
        int mid=(Left+Right)>>1;
        if (check(mid))Right=mid;
            else Left=mid;
    }
    check(Left);check(Right);
    printf("%lld",ans);
    return 0;
}

T3
在这里插入图片描述
在这里插入图片描述
有点玄学的贪心,首先,我们要所有旅客的旅行时间最小,那么在加加速器的时候,肯定要选择一条旅客经过的数量最多的那条路,然后,由于某些乘总是很迟到达一个站点,如果这个乘客实在到达得太迟,比公交车到达这个站点的时间更迟的话,那么我们就不用在这条边上加加速器了,因为即使加了你也要等到乘客来了才能走,这显然不是最优解。

#include<bits/stdc++.h> 
using namespace std;
int d[20001], getto[20001], getoff[20001], latest[20001], f[20001];
//d[i]:i - i+1这条边的长
//latest[i]:最晚到达i点的人的到达时间
//getto[i]:公交车到达i点的时间
//getoff[i]:在i点下车的人数
//f[i]:i - i+1这条边最多能减少多少时间
struct Passenger{
    int t, s, e;
}a[20001];
int n, m, k;
int main(){  
    cin>>n>>m>>k;
    for(int i=1;i<n;i++) 
	cin>>d[i];
    for (int i=1;i<=m;i++){
        cin>>a[i].t>>a[i].s>>a[i].e;
        latest[a[i].s]=max(latest[a[i].s],a[i].t);
        getoff[a[i].e]++;
    }
    for (int i=1;i<=n;i++)
        getto[i]=max(getto[i-1],latest[i-1])+d[i-1];
    while (k --){
        for (int i=n;i>=2;i--)
            if (!d[i-1]) f[i-1]=0;
            else{
                f[i-1]=getoff[i];
                if (getto[i]>latest[i]) f[i-1]=f[i-1]+f[i];
            }
        int maxtime=0,pos=0;
        for (int i=1;i<n;i++)
            if (f[i]>maxtime){
                maxtime=f[i];
				pos=i;
            }
        if (pos==0) break;
        d[pos]--;
        for (int i=pos+1;i<=n;i++) 
		getto[i]=max(getto[i-1],latest[i-1])+d[i-1];
    }
    int tot=0;
    for (int i=1;i<=m;i++)
        tot+=getto[a[i].e]-a[i].t;
    cout<<tot;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值