NOIP 2012 Day2

tags:

  • 扩展欧几里得
  • 二分答案
  • 查分
  • 倍增
  • 二分答案
  • 贪心
  • NOIP
    categories:
  • 信息学竞赛
  • 总结

同余方程
借教室
疫情控制

同余方程

Solution

  首先同余式可以转化为等式.

[axequiv 1mod bLeftrightarrow ax+by=1 ]

  根据扩展欧几里得定理, 对于式

[ax+by=k(a,b),kin mathbf{R}$$一定存在整数解.然而题面说一定存在解, 也就是说$(a,b)=1$, 然后就可以利用**扩展欧几里得**递归求得一组解.利用这组解加上**取模**, 就可以获得最小整数解. #### Code ```c++ #include<cstdio> void exgcd(int a,int b,int &x,int &y){ if(!b){ x=1,y=0;return ; } exgcd(b,a%b,y,x); y-=x*(a/b); } int main(){ int a,b,x,y; scanf("%d%d",&a,&b); exgcd(a,b,x,y); printf("%d",(x%b+b)%b); return 0; } ``` ### 借教室 #### Solution   可以发现近些年 NOIP 总是出**二分答案**的题.   其实就是给出一些操作, 每次对一定区间减去一个数, 求在哪次操作之后产生了负数.然而可以用线段树强行做, 也可以用一些巧妙一点的办法. - 线段树, 只需要有**区间加操作**和查询**区间最小值**操作, 一般线段树可以拿到95分, 还可以用可以各种**卡常技巧**, **zkw线段树**或者是**标记永久化**来加快. - 二分一个值$ ext{T}$, 表示前$ ext{T}$次借教室后会不会出现不合法情况(*即某天教室只剩下负数间*), 然后用**差分**借完$T$次教室后每一天剩下的教室数.这个一般情况是不会被卡的.**注意对于答案的记录.** #### Code ```c++ #include<cstring> #include<cstdio> #define N 1000055 #define inf 0x3f3f3f3f #define int long long struct Node{ int l,r,s; void init(){scanf("%lld%lld%lld",&s,&l,&r);} }s[N]; int n,m,d[N]; int qi[N]; int ans; int min(int a,int b){ return a<b?a:b; } bool check(int tim){ qi[0]=0; for(int i=1;i<=n;++i) qi[i]=d[i]-d[i-1]; for(int i=1;i<=tim;++i) qi[s[i].l]-=s[i].s,qi[s[i].r+1]+=s[i].s; int he=0; for(int i=1;i<=n+1;++i){he+=qi[i];if(he<0){ans=min(ans,tim);return false;}} return true; } main(){ ans=inf; scanf("%lld%lld",&n,&m); for(int i=1;i<=n;++i) scanf("%lld",&d[i]); for(int i=1;i<=m;++i) s[i].init(); int l=1,r=m,mid; while(l<=r){ mid=(l+r)>>1; if(!check(mid)) r=mid-1; else l=mid+1; } if(l>=m) printf("0"); else printf("-1 %lld",ans); return 0; } ``` ### 疫情控制   并不是很明白为什么一天会出两道二分答案的题目...   首先二分一个值$ ext{T}$, 表示在$ ext{T}$时刻内能封锁这棵树   还是有一个很重要的贪心策略, 就是一个点在到达根节点之前总是越往上走越好.然后根据**倍增**确定出每个点在给定时间$ ext{T}$所到达的最高点(*根节点为终点*). 必然有一些点到达不了根节点, 那么就让它来控制这个点; 必然有在不同时间到达根节点的点, 这些点可以去控制根节点的不同没被控制的子树; 所以最后找出所有**没有被控制的树点**和**能到达根节点的军队**进行贪心即可.   细节太多了, 很讨厌呐.]

原文地址:https://www.cnblogs.com/qdscwyy/p/8728111.html