大体状况
300/300
?因为是真题所以稍微简单一些吗= =
题目来源 NOIP2011 Day2
T1 factor
分析
求
(ax+by)k
中
anbm
的系数。
二项式杨辉三角展开后
((ax)+(by))k
=Cnk(ax)n(by)m
=k!n!m!anbmxnym
所以可以直接快速幂求得。
时间复杂度
O(k)
然而
k≤1000
所以暴力求也没关系吧。
代码
#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
分析
|S−Y|
是一个有谷的函数。
二分
W
是必需的。
然后就要考虑如何在区间内查询
首先想到的当然是主席树(不
然后主席树维护的话是
O(mlog2n)
的,感觉一定会被卡常。
发现一次查询是连续的m次,然后n与m是同阶的。
所以明明扫一遍对
wj≥W
的矿石做前缀和就可以了。
代码
#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(k∗SPFA(n,n))
或
O(knlogn)
首先裂点
i
为
从每个
i
向
作为可被更新到的分界线。
从源点
s
向每个
表示最大加速的限制。
从每个
(i−1)′
向
i
连一条容量为
表示每个加速器在这个点带来的收益
从每个
i
向汇点
作为加速器的结束位置。
然后套模板费用流。
代码
#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;
}