2018.6.18题解(HGOI)

今天做的是11年day2的题(不愧是day2,做得完全没有思路,首测只有一百分。


第一题——计算系数(factor)

  • 其实这一道题就是数学的多项式定理,展开一下就知道了规律,然后该递推递推,该快速幂就快速幂简单ac
  • 好吧我还是细讲一下。给定的是 ( a x + b y ) k 的公式,而展开之后就是 a k x k + s 2 a k 1 x k 1 b y + . . . (这里的s是前置系数)。手打几个k就可以发现s系数是杨辉三角的原理,那么就可以用递推求得 s [ k ] [ m + 1 ] 而后面的a和b就通过快速幂来求。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
void fff(){
    freopen("factor.in","r",stdin);
    freopen("factor.out","w",stdout);
}
const int MAXN=100020;
const int LIMIT=10007;
int n,m,k,a,b;
int s[1010][1010];
int ans;//mod LIMIT
void init(){
    memset(s,0,sizeof(s));
    s[1][1]=1;
    s[1][2]=1;
    for (int i=2;i<=k;i++){
        for (int j=1;j<=i+1;j++)
            s[i][j]=(s[i-1][j]+s[i-1][j-1])%LIMIT;
    }
}
int p(int t,int step){
    if(step==1){
        return t%LIMIT;
    }
    int temp=p(t,step/2)%LIMIT;
    int ss=temp*temp%LIMIT;
    if(step%2==1){
        return ss*(t%LIMIT)%LIMIT;
    }
    return ss;
}
int main(){
    fff();
    scanf("%d%d%d%d%d",&a,&b,&k,&n,&m);
    init();
    ans=1;
    ans=(p(a,n)%LIMIT)*(p(b,m)%LIMIT)%LIMIT;
    ans*=s[k][m+1]%LIMIT;
    ans=ans%LIMIT;
    cout<<ans;
    return 0;
}

第二题——聪明的质监员(qc)

  • 这道题其实本质上比我想的要简单…是我想得太多缩手缩脚的不敢下手。
  • 简述下题意,让你枚举w使得给定的m段区间里符合 Y i = j 1 j v [ j ] ( j [ L i , R i ] w j > W )
  • 而最终所得的 Y = i = 1 m Y i 与给定的S相差最小

  • 这第一眼就知道是二分题,但是这个数据有点大我有点怕…最终打了再求单个w对应的Y用了 n 2 的算法十分愚蠢。看了标程才知道这道题用前缀和来优化。

  • 先讲二分,可以知道w越大Y就越小,那么就只需要找到离S最近的Y(w)>S就可以了,然后再来看Y(w+1)和他谁近就好了。
  • 讲下前缀和:枚举1到 i ,设cnt是合法个数,sum是合法总和。查询的时候减下就行了。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
void fff(){
    freopen("qc.in","r",stdin);
    freopen("qc.out","w",stdout);
}
const int MAXN=200010;
struct num{
    long long w,v;
}a[MAXN];
struct siz{
    int L,R;
    bool operator < (const siz x) const {
        if(L==x.L) return R<x.R;
        return L<x.L;
    }
}b[MAXN];
int n,m;
const long long MAX_S = 1e13;
long long s,minn,maxx,ans;
long long sum[MAXN];
long long cnt[MAXN];
long long get_y(long long w){
    long long Y=0;
    memset(cnt,0,sizeof(cnt));
    memset(sum,0,sizeof(sum));
    for (int i=1;i<=n;i++){
        sum[i]=sum[i-1];
        cnt[i]=cnt[i-1];
        if(a[i].w>=w){
            sum[i]+=a[i].v;
            cnt[i]=cnt[i-1]+1;
        }
    }
    for (int i=1;i<=m;i++){
        long long s=sum[b[i].R+1]-sum[b[i].L],c=cnt[b[i].R+1]-cnt[b[i].L];
        Y+=s*c;
    }
    return Y;
}
long long Abs(long long x){
    if(x<0) x=-x;
    return x;
}
void solve(long long l,long long r){
    while (l<r-1){
        long long mid=(l+r)>>1;
        long long temp1=get_y(mid);
        if(temp1<=s){
            r=mid;

        }else{
            l=mid;
        }
    }
    ans=min(Abs(s-get_y(r)),Abs(get_y(r-1)-s));
}
int main(){
//  fff();
    scanf("%d%d",&n,&m);
    cin>>s;
    minn=MAXN*10;
    maxx=0;
    memset(sum,0,sizeof(sum));
    for (int i=1;i<=n;i++){
        scanf("%ld%ld",&a[i].w,&a[i].v);
        minn=min(minn,a[i].w);
        maxx=max(maxx,a[i].w);
    }
    for (int i=1;i<=m;i++){
        scanf("%d%d",&b[i].L,&b[i].R);
        b[i].L--;
        b[i].R--;
    }
    solve(minn,maxx);
    cout<<ans;
    return 0;
}

第三题——观光公交(bus)

  • 这道题真的是很玄学啊。本来以为要用动规的,但我同学暴力模拟维护就解决了….大概是数据水吧…
  • 先说题意吧。有n个站点n-1条路。每个站点有乘客上车有乘客下车。车子有k个氮气瓶用来加速,每瓶使路上花的时间减1。而每个乘客的出发时间在车子到之后,那么车上的每个人都要等。求总共花的时间的最小值。
  • 讲下算法。
  • 首先可以知道一点,设 t [ i ] 是在第i个点最早到达时间, t L I M I T [ i ] 是第i个点上人出发时间 T i 的最大值。那么这个出发的时间可以确定为一个公式:
    • t i = m a x ( t i 1 , t L I M I T i 1 ) + D i 1
  • 那么则可以得知从 A i B i 的旅客的旅游时间就是 t B i T i
  • 然后就是简单的暴力贪心模拟枚举操作了
  • 贪心的想法就是在人密度最多的路上使用,但由于使用后会影响 t i 的值,所以只好一瓶一瓶用。最后全部都用光了我就可以像没有氮气一样来模拟计算了。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
void fff(){
    freopen("bus.in","r",stdin);
    freopen("bus.out","w",stdout);
}
const int MAXM=10010;
const int MAXN=1010;
struct guest{
    int T,A,B;
}a[MAXM];
int n,m,k,all;
int D[MAXN];
int tLIMIT[MAXN],out_num[MAXN];
int t[MAXN];
int main(){
//  fff();
    scanf("%d%d%d",&n,&m,&k);
    all=0;
    for (int i=1;i<n;i++) scanf("%d",&D[i]);
    for (int i=1;i<=m;i++){
        scanf("%d%d%d",&a[i].T,&a[i].A,&a[i].B);
        tLIMIT[a[i].A]=max(tLIMIT[a[i].A],a[i].T);
        out_num[a[i].B]++;
        all-=a[i].T;
    }
    t[1]=out_num[1];
    for (int o=1;o<=k;o++){
        for (int i=2;i<=n;i++){
            t[i]=max(t[i-1],tLIMIT[i-1])+D[i-1];//重新更新t[i]
        }
        int pos=0,maxx=0;
        for (int i=1;i<n;i++){
            if(!D[i]) continue;
            int tt=0;
            for (int j=i+1;j<=n;j++){
                tt+=out_num[j];
                if(t[j]<=tLIMIT[j]) break;
            }
            if(tt>maxx) {//找到密度最大
                maxx=tt;
                pos=i;
            }
        }
        if(!pos) break;
        D[pos]--;//使用
    }
    for (int i=2;i<=n;i++){
        t[i]=max(t[i-1],tLIMIT[i-1])+D[i-1];
    }
    for (int i=1;i<=n;i++){
        all+=t[i]*out_num[i];
    }
    cout<<all;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42037034/article/details/80723947