tags:
- 扩展欧几里得
- 二分答案
- 查分
- 倍增
- 二分答案
- 贪心
- NOIP
categories: - 信息学竞赛
- 总结
同余方程
Solution
首先同余式可以转化为等式.
\[ax\equiv 1\mod b\Leftrightarrow ax+by=1\]
根据扩展欧几里得定理, 对于式
\[ax+by=k(a,b),k\in \mathbf{R}\]一定存在整数解.然而题面说一定存在解, 也就是说\((a,b)=1\), 然后就可以利用扩展欧几里得递归求得一组解.利用这组解加上取模, 就可以获得最小整数解.
Code
#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线段树或者是标记永久化来加快.
- 二分一个值\(\text{T}\), 表示前\(\text{T}\)次借教室后会不会出现不合法情况(即某天教室只剩下负数间), 然后用差分借完\(T\)次教室后每一天剩下的教室数.这个一般情况是不会被卡的.注意对于答案的记录.
Code
#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\n%lld",ans);
return 0;
}
疫情控制
并不是很明白为什么一天会出两道二分答案的题目...
首先二分一个值\(\text{T}\), 表示在\(\text{T}\)时刻内能封锁这棵树
还是有一个很重要的贪心策略, 就是一个点在到达根节点之前总是越往上走越好.然后根据倍增确定出每个点在给定时间\(\text{T}\)所到达的最高点(根节点为终点). 必然有一些点到达不了根节点, 那么就让它来控制这个点; 必然有在不同时间到达根节点的点, 这些点可以去控制根节点的不同没被控制的子树; 所以最后找出所有没有被控制的树点和能到达根节点的军队进行贪心即可.
细节太多了, 很讨厌呐.