TEST2018.4.14(Noip2011提高组Day2)

TEST2018.4.14(Noip2011提高组Day2)

为2019级lizw0520zzh的学生献上福利!!!

题目描述(感谢洛谷爸爸):  计算系数    聪明的质检员    观光公交


T1 计算系数:

小学奥数!!!

学过杨辉三角的同学应该很容易推出来:

存在答案数组使得  ans[i][j]=(ans[i-1][j]*a+ans[i-1][j-1]*b);

其中ans[i][j]表示i次方的第j项的系数,故而答案就是ans[k][m+1]。

接下来就是代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define dnt long long
using namespace std;
const dnt MOD=10007;
const int N=1000+23;
dnt ans[N][N];
dnt k,m,n,a,b;

void init() {
	ans[0][1]=1;
	ans[1][1]=a;ans[1][2]=b;
	for(int i=2;i<=k;i++)
		for(int j=1;j<=i+1;j++)
			ans[i][j]=(ans[i-1][j]*a%MOD+ans[i-1][j-1]*b%MOD)%MOD;
}

int main() {
	freopen("factor.in","r",stdin);
	freopen("factor.out","w",stdout);
	cin>>a>>b>>k>>n>>m;
	init();
	cout<<ans[k][m+1]<<endl;
	return 0;
}

不开long long见祖宗!


T2 聪明的质检员:

题外话:事后看了一下洛谷的题解,发现大家都在吐槽这个质检员会不会被开除的问题QAQ(我也这么觉得)。

因为对于不同的W,存在不同的S,所以很显然可以二分答案。

即二分W的值,求出每次Y的值,直到找到两个W值 left和right 使得 Y 最接近 S 。

注意这里一定要检查 left和right 所得到 Y 值与 S 的差的绝对值谁更小(被坑了T_T)。

代码:注意check()可以有多种写法,以下为其中之一

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#define maxn 210000
using namespace std;
long long s1[maxn],s2[maxn];
int l[maxn],r[maxn];
int w[maxn],v[maxn];
int n,m;
long long S;
long long check(int x) {
    memset(s1,0,sizeof(s1));
    memset(s2,0,sizeof(s2));
    for (int i=1;i<=n;i++) {
        s1[i]=s1[i-1];s2[i]=s2[i-1];
        if (w[i]>=x) {
            s1[i]+=1;
            s2[i]+=v[i];
        }
    }
    long long res=0;
    for(int i=0;i<m;i++)
        res+=(s1[r[i]]-s1[l[i]-1])*(s2[r[i]]-s2[l[i]-1]);
    return res;
}
int main(){
    freopen("qc.in","r",stdin);
    freopen("qc.out","w",stdout);
    cin>>n>>m>>S;
    for(int i=1;i<=n;i++)
        scanf("%d%d",&w[i],&v[i]);
    for(int i=0;i<m;i++)
        scanf("%d%d",&l[i],&r[i]);
    int left=0,right=*max_element(w+1,w+n+1);
    while(left+1<right){
        int mid=(left+right)/2;
        if (check(mid)<=S)right=mid;
            else left=mid;
    }
    cout<<min(abs(check(left)-S),abs(S-check(right)));
    return 0;
}


T3 观光公交:

吐槽一波:大家看着很复杂,其实巨简单,就是贪心。但是看你贪心策略是什么了。

一开始我想的是找出每段路有几个人走过,先算出总时间,然后对走的人数最多的路进行加速(注意每段路时间不能小于零)。

然后就WA了(想爆粗)。

于是考完试后照例经典算法:百度搜索了一波,发现是贪心策略有错。

正确思路大家可以参照以下网址,在此不加以赘述:

pascal语言(看思路就行)          C++(代码实现)

本人使用第二种方法依葫芦画瓢写了一下,结果在累加距离时加错了,改了好久【我们的同学,一定要细心啊(强行学老李口气)】。

代码【史上最简单写法(有点小夸张)】:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1000+233;
int n,m,k,ans=0;
int arrive[N],leave[N],off[N],d[N],sum[N];
int main() {
    //freopen("bus.in","r",stdin);
    //freopen("bus.out","w",stdout);
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<n;i++) scanf("%d",d+i);
    for(int i=1,a,b,t;i<=m;i++) scanf("%d%d%d",&t,&a,&b),off[b]++,leave[a]=max(leave[a],t),ans-=t;
    int now,use;
    while(k) {
        for(int i=1;i<=n;i++) arrive[i]=max(arrive[i-1],leave[i-1])+d[i-1];
        now=0;
        for(int i=n;i>1;i--)
            if(!d[i-1]) sum[i-1]=0;
            else {sum[i-1]=off[i];if(arrive[i]>leave[i]) sum[i-1]+=sum[i];}
        for(int i=1;i<n;i++)
            if(now<sum[i]) now=sum[i],use=i;
        if(!now) break;
        d[use]--;k--;
    }
    for(int i=1;i<=n;i++) arrive[i]=max(arrive[i-1],leave[i-1])+d[i-1];
    for(int i=1;i<=n;i++) ans+=arrive[i]*off[i];
    printf("%d",ans);
    return 0;
}


那么题解到此结束了!祝大家早日AK(完结撒花)。

猜你喜欢

转载自blog.csdn.net/y554280697/article/details/79972604
今日推荐