2017-10-17离线赛

大体状况

300/300
?因为是真题所以稍微简单一些吗= =
题目来源 NOIP2011 Day2

T1 factor

分析

(ax+by)k anbm 的系数。
二项式杨辉三角展开后
((ax)+(by))k
=Cnk(ax)n(by)m
=k!n!m!anbmxnym
所以可以直接快速幂求得。
时间复杂度 O(k)
然而 k1000 所以暴力求也没关系吧。

代码
#include<bits/stdc++.h>
using namespace std;

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define LL long long

void Rd(int &res){
    char c;res=0;
    while((c=getchar())<48);
    do res=(res<<3)+(res<<1)+(c^48);
    while((c=getchar())>47);
}

#define M 1004
#define Mod 10007
int Fac[M];
int a,b,k,n,m;
int Pow(int x,int p){
    int Res=1;
    for(int k=x%Mod;p;p>>=1,k=k*k%Mod)if(p&1)(Res*=k)%=Mod;
    return Res;
}
int main(){
    Rd(a),Rd(b),Rd(k),Rd(n),Rd(m);
    Fac[0]=1;
    REP(i,1,k+1)Fac[i]=Fac[i-1]*i%Mod;
    if(k==0)puts("1");
    else printf("%d\n",Fac[k]*Pow(Fac[n],Mod-2)%Mod*Pow(Fac[m],Mod-2)%Mod*Pow(a,n)%Mod*Pow(b,m)%Mod);
    return 0;
}

T2 qc

分析

|SY| 是一个有谷的函数。
二分 W 是必需的。
然后就要考虑如何在区间内查询wjW的矿石数量与价值和。
首先想到的当然是主席树(不
然后主席树维护的话是 O(mlog2n) 的,感觉一定会被卡常。
发现一次查询是连续的m次,然后n与m是同阶的。
所以明明扫一遍对 wjW 的矿石做前缀和就可以了。

代码
#include<bits/stdc++.h>
using namespace std;

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define LL long long

void Rd(int &res){
    char c;res=0;
    while((c=getchar())<48);
    do res=(res<<3)+(res<<1)+(c^48);
    while((c=getchar())>47);
}
#define M 200004
int n,m,W[M],V[M],L[M],R[M];
LL S;
struct P100{
    int P[M],sz;
    int Cnt[M];
    LL Sum[M];
    LL Y(int x){
        REP(i,1,n+1)
            Cnt[i]=Cnt[i-1]+(W[i]>=x),
            Sum[i]=Sum[i-1]+((W[i]>=x)*V[i]);
        LL Res=0;
        REP(i,0,m){
            Res+=
            (Sum[R[i]]-Sum[L[i]-1])*
            (Cnt[R[i]]-Cnt[L[i]-1]);
        }
        return Res;
    }
    void Solve(){
        REP(i,1,n+1)P[i]=W[i];
        sort(P+1,P+n+1);
        sz=unique(P+1,P+n+1)-P-1;
        REP(i,1,n+1)W[i]=lower_bound(P+1,P+sz+1,W[i])-P;

        int l=1,r=sz;
        LL Ans=S;
        while(l<=r){
            int Mid=l+r>>1;
            LL Tmp=Y(Mid);
            chkmin(Ans,abs(S-Tmp));
            if(Tmp>S)l=Mid+1;
            else r=Mid-1;
        }
        REP(x,max(1,l-2),min(sz+1,l+2))chkmin(Ans,abs(S-Y(x)));
        printf("%lld\n",Ans);
    }
}P100;
int main(){
    Rd(n),Rd(m),scanf("%lld",&S);
    REP(i,1,n+1)Rd(W[i]),Rd(V[i]);
    REP(i,0,m)Rd(L[i]),Rd(R[i]);

    P100.Solve();
    return 0;
}

T3 bus

分析
P60

这题贪心的思路还是比较显然的。
处理出该加速器影响的区间(即均满足 Timei>Mxi )后,
加速器之间不会互相影响。
因此满足最优性,那么直接贪心的复杂度为 O(nmk)O(n2k)
即枚举每个位置计算其减少量,然后使用加速器。

P100

上面的思路稍微改改就能得到 O(nk) 的简单做法。
处理出区间后,显然在更左用加速器更优。
用一个指针扫过去得到每个位置的值即可。
然后这样就能过所有数据。

代码
#include<bits/stdc++.h>
using namespace std;

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define LL long long

void Rd(int &res){
    char c;res=0;
    while((c=getchar())<48);
    do res=(res<<3)+(res<<1)+(c^48);
    while((c=getchar())>47);
}

#define N 1004
#define M 10004

int n,m,k,D[N],Cnt[N],Mx[N],Tim[N];
int T[M],A[M],B[M];

bool Delete(){
    REP(i,1,n) Tim[i+1]=max(Tim[i],Mx[i])+D[i];
    int Pos=0,Max=-1;
    for(int i=1,j;i<n;i++) if(D[i]>0){
        j=i+1;
        int Res=Cnt[j];
        while(Tim[j]>Mx[j]) Res+=Cnt[++j];
        if(Res>Max) Max=Res,Pos=i;
        i=j-1;
    }
    if(Max==-1)return 1;
    D[Pos]--;
    return 0;
}
int main(){
    Rd(n),Rd(m),Rd(k);
    REP(i,1,n)Rd(D[i]);
    REP(i,0,m){
        int t,a,b;
        Rd(t),Rd(a),Rd(b);
        Cnt[b]++;
        chkmax(Mx[a],t);
        T[i]=t,A[i]=a,B[i]=b;
    }
    REP(i,0,k)if(Delete())break;

    REP(i,1,n) Tim[i+1]=max(Tim[i],Mx[i])+D[i];

    int Ans=0;
    REP(i,0,m) Ans+=Tim[B[i]]-T[i];
    printf("%d\n",Ans);
    return 0;
}
优化

注意到上面的方法进行了很多次重复的计算Time,
以及重复地查找那些可能已知的值,可以对于其采用区间更新的方法。
区间更新仍然要在所有当前可用的区间中寻找最大值。
所以可以用一个堆来存放当前所有区间。
每次提取出乘客数最大的区间,查看其是否合法并用最小的可能值更新。
然后拆成两半。
理论复杂度最差为 O(n2logn) ,实际上非常快。

代码
#include<bits/stdc++.h>
using namespace std;

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define LL long long

void Rd(int &res){
    char c;res=0;
    while((c=getchar())<48);
    do res=(res<<3)+(res<<1)+(c^48);
    while((c=getchar())>47);
}

#define N 1004

int n,m,k,D[N],Cnt[N],Mx[N],Tim[N],Ans;
struct Node{
    int l,r,x;
    bool operator <(const Node &_)const{
        return x<_.x;
    }
};
priority_queue<Node>Q;
int main(){
    Rd(n),Rd(m),Rd(k);
    REP(i,1,n)Rd(D[i]);
    REP(i,0,m){
        int t,a,b;
        Rd(t),Rd(a),Rd(b);
        Cnt[b]++;
        chkmax(Mx[a],t);
        Ans-=t;
    }
    REP(i,1,n) Tim[i+1]=max(Tim[i],Mx[i])+D[i];
    REP(i,1,n+1) Ans+=Cnt[i]*Tim[i];
    REP(i,2,n+1) Cnt[i]+=Cnt[i-1];

    Q.push((Node){1,n,Cnt[n]-Cnt[1]});
    while(!Q.empty() && k){
        Node T=Q.top();Q.pop();
        int l=T.l,r=T.r,x=T.x;
        int Pos=-1,Use=min(k,D[l]);

        REP(i,l+1,r)if(Use>Tim[i]-Mx[i])
            Use=Tim[i]-Mx[i],Pos=i;

        if(Use>0){//满足要求,可以更新
            D[l]-=Use;
            k-=Use;
            Ans-=x*Use;
            REP(i,l,r)Tim[i+1]=max(Tim[i],Mx[i])+D[i];
        }
        if(~Pos){//拆为两个区间
            Q.push((Node){l,Pos,Cnt[Pos]-Cnt[l]});
            Q.push((Node){Pos,r,Cnt[r]-Cnt[Pos]});
        }
        else if((++l)<r)Q.push((Node){l,r,Cnt[r]-Cnt[l]});
    }
    printf("%d\n",Ans);
    return 0;
}
另一个方法

这个贪心还可以用最小费用最大流来做。
然而比暴力还慢,
最差复杂度为 O(kSPFA(n,n)) O(knlogn)
首先裂点 i i i ,方便限制的添加。
从每个 i i连一条容量为 max(0,TimeiMxi) ,费用为0的边,
作为可被更新到的分界线。
从源点 s 向每个i连一条容量为 Di ,费用为0的边,
表示最大加速的限制。
从每个 (i1) i 连一条容量为INF,费用为 Cnti 的边,
表示每个加速器在这个点带来的收益
从每个 i 向汇点t连一条容量为 INF ,费用为0的边,
作为加速器的结束位置。
然后套模板费用流。

代码
#include<bits/stdc++.h>
using namespace std;

#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmax(a,b) a=max(a,b)
#define chkmin(a,b) a=min(a,b)
#define LL long long
#define INF 0x3f3f3f3f

void Rd(int &res){
    char c;res=0;
    while((c=getchar())<48);
    do res=(res<<3)+(res<<1)+(c^48);
    while((c=getchar())>47);
}

#define M 1044

struct Edge{
    int u,v,w,c;
}E[(M<<3)+44];
template<const int Len,const int Num>struct Linklist{
    int G[Len],Next[Len],Head[Num],tot;
    int operator [](int x){return G[x];}
    void pb(int u,int id){Next[++tot]=Head[u],G[Head[u]=tot]=id;}
    #define LREP(i,a,G) for(int i=G.Head[a];i;i=G.Next[i])
};
Linklist<(M<<3)+44,(M<<1)>G;

int n,m,k,etot;
void Add_Edge(int u,int v,int w,int c){
    assert(etot<((M<<3)+44));
    E[etot]=(Edge){u,v,w,c}; G.pb(u,etot++);
    E[etot]=(Edge){v,u,0,-c};G.pb(v,etot++);
}
int Dis[M<<1];
bool Vis[M<<1];
int Fa[M<<1];
int s,tp,t,Ans;
queue<int>Q;
void MCF(int f){
    while(f>0){
        while(!Q.empty())Q.pop();
        memset(Dis,63,sizeof(Dis));
        Dis[s]=0,Q.push(s);
        while(!Q.empty()){
            int A=Q.front();Q.pop();
            Vis[A]=0;
            LREP(i,A,G){
                Edge e=E[G[i]];
                if(!e.w)continue;
                int B=e.v;
                if(Dis[B]>Dis[A]+e.c){
                    Dis[B]=Dis[A]+e.c;
                    Fa[B]=G[i];
                    if(!Vis[B])Q.push(B),Vis[B]=1;
                }
            }
        }
        if(Dis[t]==INF)break;

        int x=t,Dec=f;
        while(x!=s){
            chkmin(Dec,E[Fa[x]].w);
            x=E[Fa[x]].u;
        }
        x=t;
        while(x!=s){
            E[Fa[x]].w-=Dec,E[Fa[x]^1].w+=Dec;
            Ans+=Dec*E[Fa[x]].c;
            x=E[Fa[x]].u;
        }
        f-=Dec;
    }
}
int D[M],Tim[M],Cnt[M],Mx[M];
int main(){
    Rd(n),Rd(m),Rd(k);
    REP(i,1,n)Rd(D[i]);
    REP(i,0,m){
        int t,a,b;
        Rd(t),Rd(a),Rd(b);
        Cnt[b]++;
        chkmax(Mx[a],t);
        Ans-=t;
    }
    REP(i,1,n) Tim[i+1]=max(Tim[i],Mx[i])+D[i];
    REP(i,1,n+1) Ans+=Cnt[i]*Tim[i];
    s=0,t=(n<<1)+4,tp=t-1;

    Add_Edge(tp,t,k,0);
    REP(i,1,n+1) Add_Edge(i,i+n,max(0,Tim[i]-Mx[i]),0);
    REP(i,2,n+1) Add_Edge(i-1+n,i,INF,-Cnt[i]);
    REP(i,1,n+1) Add_Edge(i,tp,INF,0);
    REP(i,1,n) Add_Edge(s,i+n,D[i],0);

    MCF(k);
    printf("%d\n",Ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值