NOIP2011
铺地毯
Link
模拟。
#include<cstdio>
const int N=10007;
int a[N],b[N],g[N],k[N];
int read(){int x;scanf("%d",&x);return x;}
int main()
{
int n=read(),x,y;
for(int i=1;i<=n;++i) a[i]=read(),b[i]=read(),g[i]=read(),k[i]=read();
x=read(),y=read();
for(int i=n;i;--i) if(a[i]<=x&&x<=a[i]+g[i]&&b[i]<=y&&y<=b[i]+k[i]) return !printf("%d",i);
puts("-1");
}
选择客栈
Link
枚举右边的客栈的位置,同时记录这个位置的左边各个色调的客栈有\(cnt\)个。
同时我们还需要维护各个色调的客栈中到当前位置中存在合法咖啡店的有\(sum\)个,这可以通过维护左边的最近的咖啡店位置来完成。
具体来讲当我们到达一个色调为\(a\)的有合法咖啡点的客栈时,令\(sum_a\leftarrow cnt_a\)即可。
#include<cstdio>
const int N=10007;
int las[N],sum[N],cnt[N];
int read(){int x;scanf("%d",&x);return x;}
int main()
{
int n=read(),m=(read(),read()),now=0, ans=0;
for(int i=1;i<=n;++i)
{
int a=read(),b=read();
if(b<=m) now=i;
if(now>=las[a]) sum[a]=cnt[a];
las[a]=i,ans+=sum[a],++cnt[a];
}
printf("%d",ans);
}
Mayan游戏
Link
爆搜,没写。
计算系数
Link
二项式定理。
#include<cstdio>
const int N=1007,P=10007;
int C[N][N];
int read(){int x;scanf("%d",&x);return x;}
int pow(int a,int b){int c=1;for(;b;b>>=1,a=a*a%P)if(b&1)c=c*a%P;return c;}
int main()
{
int a=read()%P,b=read()%P,k=read(),n=read(),m=read();
for(int i=0;i<=k;++i) for(int j=C[i][0]=1;j<=i;++j) C[i][j]=(C[i-1][j]+C[i-1][j-1])%P;
printf("%d",pow(a,n)*pow(b,m)%P*C[k][n]%P);
}
聪明的质检员
Link
先二分答案(判断当前的\([y>s]\))。
检查的话维护一下\([w_i\ge W],[w_i\ge W]v_i\)的前缀和即可。
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
using i64=long long;
const int N=200007;
i64 read(){i64 x=0;char c=getchar();while(isspace(c))c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return x;}
int n,m,l[N],r[N],w[N],v[N];i64 s,ans=1e18;
int check(int W)
{
static int cnt[N];static i64 sum[N];i64 y=0;
memset(cnt+1,0,4*n),memset(sum+1,0,8*n);
for(int i=1;i<=n;++i) cnt[i]=cnt[i-1]+(w[i]>=W),sum[i]=sum[i-1]+(w[i]>=W)*v[i];
for(int i=1;i<=m;++i) y+=(cnt[r[i]]-cnt[l[i]])*(sum[r[i]]-sum[l[i]]);
return ans=std::min(ans,llabs(s-y)),y>s;
}
int main()
{
n=read(),m=read(),s=read();
for(int i=1;i<=n;++i) w[i]=read(),v[i]=read();
for(int i=1;i<=m;++i) l[i]=read()-1,r[i]=read();
for(int l=0,r=1000000,mid;l<=r;) mid=(l+r)/2,check(mid)? l=mid+1:r=mid-1;
printf("%lld",ans);
}
观光公交
Link
每次把加速器用在可以是答案减少最多的地方就即可。
具体而言,我们处理出:
\(sum_i\)到\(i\)为止下车人数之和。
\(t_i\)在\(i\)最晚的上车的人的上车时间。
\(a_i\)表示到达\(i\)的时间。
那么我们做\(k\)次,每次先处理出:
\(l_i\)表示在\(i\)使用加速器,能够使得出发时间减一的最后位置。
然后找贡献最大的,更新\(t,a,l\)即可。
考虑性质,原图由于必须要等乘客的缘故,肯定被分成若干段。
每段选最靠左的端点必定最优,我们找出最优的段,一直加满,直到新增了一段。
这样每次操作必定增加一段,只会进行\(O(n)\)次。
由于段之间是没有影响的,所以可以用优先队列来维护不同的段的最大值,再用线段树维护每段还需要加多少才会满。
这样时间复杂度就是\(O(n\log n)\)。
#include<queue>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<numeric>
#include<algorithm>
using i64=long long;
const int N=100007;
int D[N],latest[N],wait[N],cnt[N];
char ibuf[1<<22],*iS=ibuf;
int read(){int x=0;while(isspace(*iS))++iS;while(isdigit(*iS))(x*=10)+=*iS++&15;return x;}
namespace Seg_Tree
{
int mn[N<<2],pos[N<<2],tag[N<<2];
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)/2)
void pushup(int p){mn[p]=std::min(mn[ls],mn[rs]),pos[p]=mn[p]==mn[rs]? pos[rs]:pos[ls];}
void modify(int p,int x){tag[p]+=x,mn[p]-=x;}
void pushdown(int p){if(tag[p])modify(ls,tag[p]),modify(rs,tag[p]),tag[p]=0;}
void build(int p,int l,int r)
{
if(l==r) return mn[p]=wait[l],pos[p]=l,void();
build(ls,l,mid),build(rs,mid+1,r),pushup(p);
}
void update(int p,int l,int r,int L,int R,int x)
{
if(L>r||l>R||L>R) return ;
if(L<=l&&r<=R) return modify(p,x);
pushdown(p),update(ls,l,mid,L,R,x),update(rs,mid+1,r,L,R,x),pushup(p);
}
int find(int p,int l,int r,int L,int R)
{
if(L>r||l>R||L>R) return 0;
if(L<=l&&r<=R) return p;
pushdown(p);
int p1=find(ls,l,mid,L,R),p2=find(rs,mid+1,r,L,R);
return mn[p1]>mn[p2]? p2:p1;
}
#undef ls
#undef rs
#undef mid
}using namespace Seg_Tree;
struct node{int l,r;node(int x=0,int y=0):l(x),r(y){}};
int operator<(const node&a,const node&b){return cnt[a.r]-cnt[a.l]<cnt[b.r]-cnt[b.l];}
std::priority_queue<node>q;
int main()
{
fread(ibuf,1,1<<22,stdin);
int n=read(),m=read(),k=read(),las=1;i64 ans=0;mn[0]=1e9;
for(int i=1;i<n;++i) D[i]=read();
for(int i=1,t,x,y;i<=m;++i) t=read(),x=read(),y=read(),latest[x]=std::max(latest[x],t),++cnt[y],ans-=t;
std::partial_sum(cnt+1,cnt+n+1,cnt+1);
for(int i=2;i<=n;++i)
{
wait[i]=std::max(0,wait[i-1]+latest[i-1]+D[i-1]-latest[i]);
if(!wait[i]) q.emplace(las,i),las=i;
ans+=1ll*(cnt[i]-cnt[i-1])*(wait[i-1]+latest[i-1]+D[i-1]);
}
if(las<n) q.emplace(las,n);
build(1,2,n);
while(k&&!q.empty())
{
int l=q.top().l,r=q.top().r,x=find(1,2,n,l+1,r-1),d=std::min(k,std::min(D[l],mn[x])),p=d==D[l]? l+1:pos[x];
q.pop(),ans-=1ll*d*(cnt[r]-cnt[l]),k-=d;
if(!k) break;
if(d) update(1,2,n,l+1,r-1,d),D[l]-=d;
if(l<p) q.emplace(l,p);
if(p<r) q.emplace(p,r);
}
printf("%lld",ans);
}