2017-10-17离线赛

来源:互联网 发布:java nanotime 转毫秒 编辑:程序博客网 时间:2024/06/06 16:48

大体状况

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

T1 factor

分析

(ax+by)kanbm的系数。
二项式杨辉三角展开后
((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 longvoid 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 10007int 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 longvoid 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 200004int 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 longvoid 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 10004int 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 longvoid 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 1004int 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)
首先裂点iii,方便限制的添加。
从每个ii连一条容量为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 0x3f3f3f3fvoid 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 1044struct 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;}
原创粉丝点击