Day 2
考试题解
T1 小路灯
再次签到成功。考试时做法是二分然后加上一点点小贪心。不过这个贪心调了一万年。一开始用半小时写出了“正解”,然后开始写暴力造数据。结果第二组数据就拍出问题了。于是开始调试。调了两个小时才终于调出来,然后开始看(T2)。
不过真的就只是二分吗?中间应该还有一些细节要注意。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
inline ll read(){
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
ll n,k,minn=2000000005,maxx=0,ans=2000000005;
ll a[100005];
bool check(ll x){
ll t=a[1],sum=0;
for(ll i=2;i<=n;i++){
if(a[i]-t>x){
sum++;
t=a[i-1];
for(ll j=i,k1=i;j<=n;j=k1){
k1=j;
while(a[k1]-t<=x&&k1<=n){
k1++;
}
ll k2=k1;
if(k1>j){
while(a[k2+1]-a[k1]<=x&&k2+1<=n){
k2++;
}
}
if(k2>n) break;
k1=k2;
sum++;
t=a[k2];
}
break;
}
}
if(sum<=k) return true;
else return false;
}
int main()
{
scanf("%lld%lld",&n,&k);
for(ll i=1;i<=n;i++){
scanf("%lld",&a[i]);
minn=min(minn,a[i]);
maxx=max(maxx,a[i]);
}
ll l=0,r=maxx-minn;
while(l<=r){
ll mid=(l+r)/2;
if(check(mid)){
ans=mid;
r=mid-1;
}
else{
l=mid+1;
}
}
printf("%lld
",ans);
return 0;
}
老师做法(没有那么多细节要注意,更好写):在这个距离内往后找,找到一个路灯然后点亮它即可。不需要像我一样考虑前后的最优选择。
类似题目:(NOIp) 2015 D2T1 跳石头
T2 序列
40pts白扔,暴力分-=40。考试的时候真的有点急了,主要是(T1)花费了太多时间。不过这的确是值得的(因为(T1)暴力一分都没有)。暴力模拟就可以得到(40pts)。但是nc写挂了。
40分做法:
暴力模拟。
60分做法:
对每次操作用线段树或者平衡树维护。
100分做法:
发现每一次操作都是将所有的数向后移一位。然后在这种模拟下有特殊情况,每个序列都有(n/k)个数需要挪到每一段后面单独处理,这样总复杂度就是(O(n/1+n/2+n/3+...)=O(n*ln(n))),开一个二倍数组维护即可。
T3 路灯
靠着(T1)对拍用的暴力改了改,水了(60)pts。爆搜就是60分做法。
100做法:
我就知道是(dp),但是我不会。f_{i,j}表示前(i)个路灯点亮了(j)个,而且第(i)个是点亮的。(f_{i,j}=min(f_{r,j-1}+h_{r,i}))。需要提前预处理(h)数组,然后总时间复杂度是(O(n^2*k))。
类似题目:(NOIp) 2015 子串(拓展:改成子序列)
(f_{i,j,k}=f_{i-1,j-1,k-1})
T4 匹配
考试由于时间原因写假了且来不及改(20分做法)。
20分做法:
暴力求出两两之间距离然后(n^3)枚举。
40分做法:
暴力求出两两之间的距离(此过程复杂度?),枚举三个点的中间点,然后找到所有与中间点距离为2的点(考试时我也曾有过此思路,但是不太好写)。
100分做法:
一个点与一个子树中距离为2的所有点权和和
树形dp或边加边维护
拓展:洛谷P3565 [POI2014]HOT-Hotels
搜索专题补充
记忆化搜索
实质是(dp)。
slots
啥也没听懂?????????
计数
记录13种牌每一种剩多少张,同时记录上一张牌是什么。每当已经搜索过这个状态就不用再搜索了。
优化:用一个数组(a_i)来记录面值相同牌数为i的时候的方案数。
idfs
埃及分数
用(s)来记录和,用(a)来记录当前分母,(k)记录个数,然后剪枝:如果(1/a*k<s),那么肯定不行了。而且还要让分母尽可能小。
八数码问题
k短路问题
优先队列,取出队首然后最短路。第(k)次到达就是(k)短路
骑士精神
剪枝:估价函数大于剩余搜索层数。
Lizard 。。。
先搜索出前一半三个人的收益差,然后再搜索后一半,然后比较。然后(Hash)查询(不会)。
随机算法(骗分)
用正确性换取时间复杂度。
爬山算法
(N)个点,有权值,选一个点,使其他点到他的距离 乘上 权值 的和最小。
模拟退火
[HAOI2006]均分数据
???????????????????????????????????????????????????????????????????
例题
背包问题变搜索
(max) (x_1+3x_2+5x_3+9x_4)//价值最大化
(2x_1+3x_2+4x_3+7x_4<=10)//体积
搜索:当前搜到了第几层,剩余体积,得到价值
剪枝:先按价值排序,如果剩余体积乘以当前物品价值仍然小于已得到的价值,那么剪去
货郎问题
剪枝:用(L_i)来记录(i)的出边的最小值,记录当前已走的路径长度,如果长度加上每一个剩余顶点向外连的最小值已经比答案大了,那么直接减去。
邮票面值设计
剪枝:确定一个可选的区间
Tales。。。
求奇最短路和偶最短路((NOIp) 2019 普及组(T4) 加工零件)
建两个图(分层图思想)
NOIp 2014 寻找道路
删掉不符合条件的点然后跑最短路(画图
NOIp 2015 斗地主
按顺序搜索
数论
快速幂
越狱
求出总情况和不会越狱的情况,然后相减得到会越狱的情况。
总:(m^n)
不会越狱:(m*(m-1)^(n-1))
用快速幂求解即可
素数
线性筛
积性函数
莫比乌斯函数:0,n>1;1,n==1
积性函数性质:如果能够求出f(p^k),那么就可以快速地求出f(1)到f(n)。
例题:sigma那个题
看到gcd就枚举。(gcd(i,n)=d -> gcd(i/d,n.d)=1 -> 设i=kd) -> 枚举
??????????????????????????????
逆元
(xy≡1(mod n))
(x)在模(n)意义有逆元当且仅当((x,n)=1)
(a=[n/x],b=x%a)
(ax+b=0(mod n))
(ax=-b)
(ax*b^{-1}=-b*b^{-1})
(ax*b^{-1}=-1)
(-ax*b^{-1}=1(mod n))
所以(x)的逆元是(-a*b^{-1})
线性求逆元代码
inv[1]=1;
for(i=2;i<=n-1;i++)
inv[i]=(n-inv[n%i]*(n/i)%n)%n;
(O(1))计算组合数(模意义下)
Lucas定理
设(P)是质数,则(C_n^m)%(P)= (C_{nmodP}^{mmodP}) * (C_{n/P}^{m/P}) % (P)
常用来求(n),(m)较大,但(P)较小的组合数取模
最大公约数和最小公倍数
定理: (ab = gcd(a,b) * lcm(a,b))
求最大公约数:利用公式(gcd(a, b)=gcd(b, a mod b)), 时间复杂度为(O(logb))
威尔逊定理:若(p)为素数,则((p-1)!≡-1 (mod p))。
费马小定理:(p)是素数且((a,p)=1),则(ap-1≡1 (mod p))
线性求欧拉函数:
void eular(int n)
{for(int i=2;i<=n;i++)
{if (!IsPrime[i])
{ prime[++cnt]=i; phi[i]=i-1;}
for(int j=1;j<=cnt;j++)
{ if (prime[j]*i>n) break;
Isprime[prime[j]*i]=1;
if (i%prime[j]==0)
{phi[i*prime[j]]=phi[i]*prime[j]; break;}
else
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
Euler定理: 若(gcd(a, n)=1)则(a^n = 1 (mod n))
意义:当(b)很大时,(a^b) = $a^{ (bmodn) }$ ((mod n)),让指数一直比较小
裴蜀定理:设(gcd(a,b)=d),一定存在整数(x,y),使得(ax+by=d) 。拓展GCD是求特解的过程。
int ex_gcd(int a, int b, int &x, int & y)
{if (b==0){ x = 1; y = 0; return a; }
else {int r =ex_gcd(b, a%b, x, y);
int t = x; x = y; y = t – a/b*y;
return r;
}
}
P1516 青蛙的约会
设总共跳(T)次可以相遇,则有:(A)的坐标(X+MT),(B)的坐标(Y+NT)相遇的充要条件:(X+MT-Y-NT=PL ( p是整数)) ,变形为((N-M)*T+LP=X-Y(L>0)),利用扩展欧几里德原理,求出最小的(T)即可。
(a1_{x1}+a2_{x2}+…+an_{xn}=N),有整数解的充分必要条件是((a1,a2,…,an)|N)
中国剩余定理
(a≡a1 (mod n1))
(a≡a2 (mod n2))
……………
(a≡ak (mod nk))
(a=(a1*c1+a2*c2+…+ak*ck) mod (n))…中国剩余定理
其中(n= n1*n2*…*nk) ,(ci=mi*(mi-1 mod ni)),(mi=n/ni)
int remainder()//求同余方程组a
{int i,j,n=1,m,d,x,y;
for (i=1;i<=k;i++) n*=n[i];
a=0;
for (i=1;i<=k;i++)//将同余方程组转化为对应的多项式求值
{m=n/n[i];//求mi
d=ex_gcd(n[i],m,x,y);//通过欧几里得扩展形式求y=mi-1
a=(a+y*m*a[i])%n//累加a的值
}
if (a>0) return a;
else return a+n;
}
炸 裂
容斥原理
错排问题
有(n)个物品和(n)个盒子,编号都是从(1)到(n)。现在要把每个物品放进一个盒子,每个盒子刚好放一个物品,并且(i)号物品不能放进(i)号盒子。求方案数模1000000007。
(n<=100000)
(n!)-(C_n^1*(n-1)!+C_n^2*(n-2)!-......+...)
阶乘,组合数都可以预处理(逆元方法(O(1))求组合数)
隔板法
有(m)个未知数(x_1,x_2,……,x_m)。满足(x_1+x_2+x_3....+x_m=k) (0<=xi<=n)。求方程解的个数。(1<=n,m<=100000)。(0<=k<=100000)。
相当于有(k-1)个空,插进去(m-1)个板,答案即为(C_{k-1}^{m-1})
拓展:(xi>=n)
做法:(k)减去(m*n)即可转化为原题
概率与期望
如果一个随机事件发生的概率是(p),那么重复做这个实验,期望做多少次才能第一次发生该事件?
(1*p+2*(1-p)*p+3*(1-p)^2*p+4*(1-p)^3*p+……=1/p)
收集邮票
???????????????????????????????????
小左的GCD
似乎可做,求(1≤x,y≤N)且(gcd(x,y))为素数的数对((x,y))的个数,就相当于是在(1<=x,y<=n/i)中找互素对,用欧拉函数可以(O(n))求解。