A.Heist
CF原题链接
题目大意:
给出一个长度为\(n\)的序列\(a\),已知序列\(a\)是由一个首项为\(x\)、常数为\(1\)的等差数列删除若干个数得来,求删数的最少个数。
解题思路:
直接做做完了啊,为什么这题不是红题 既然要让删的数最少,那就要让原序列长度尽量小。我们钦定首项\(x\)为\(min_{a}\),末项为\(max_{a}\),然后给原序列升序排序,\(\Sigma^{n}_{i=2}(a_{i}-a_{i-1}-1)\)就是答案。
没啥用的代码
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1010;
int n,a[N];
int ans;signed main()
{scanf("%lld",&n);for (int i=1;i<=n;i++) scanf("%lld",&a[i]);sort(a+1,a+1+n);for (int i=2;i<=n;i++) ans+=a[i]-a[i-1]-1;printf("%lld",ans);return 0;
}
B.Buying a TV Set
CF原题链接
题目大意:
给出4个数\(a,b,c,d\),求满足\(\frac{x}{y}=\frac{c}{d},x\leqslant a,y\leqslant b\)的\((x,y)\)数量。
解题思路:
直接做做完了啊,为什么这题不是红题 容易想到,有一种东西叫做约分。那么我们钦定\(\frac{c}{d}\)是最简分数(不是的话就给它约分一下),那么\(x,y\)一定分别是\(c,d\)的倍数。然后就出来了,答案就是\(min(\lfloor\frac{a}{c}\rfloor,\lfloor\frac{b}{d}\rfloor)\)。
没啥用的代码
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
int a,b,c,d;int gcd(int x,int y)
{if (y==0) return x;return gcd(y,x%y);
}
signed main()
{scanf("%lld%lld%lld%lld",&a,&b,&c,&d);int gd=gcd(c,d);c/=gd,d/=gd;printf("%lld",min(a/c,b/d));return 0;
}
C.Coffee Break
CF原题链接
题目大意:
给出\(n,m,d\)与一个长度为\(n\)的序列\(a\),每次删除操作可以从\(a\)中删除若干个数\(del\),使得这些数升序排序后\(del_{i}-del_{i-1}>d\)。求最少删除完整个序列\(a\)的操作次数,与每个数是在第几个操作删除的。\((1\leqslant n\leqslant 2\times 10^{5},1\leqslant d\leqslant m\leqslant 10^{9})\)
解题思路:
注意到时限为2s。记录序列\(a\)时顺便记录位置,然后升序排序\(a\)。可以想到对于\(a_{i}\)它一定可以影响到\(a_{j}\)满足\(a_{j}>a_{i}+d\),那么对于每个\(a_{i}\)二分查找第一个\(a_{j}\)并标记(每次查找一个即可),若当前\(a_{i}\)未被标记,说明需要一个新的操作,此时\(cnt++\)即可。
不做评价的代码
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int n,m,d;
struct node{int x,d;int used;bool operator < (const node &b) const{return x<b.x;}
}a[N];
int cnt;
int ans[N];signed main()
{scanf("%lld%lld%lld",&n,&m,&d);for (int i=1,x;i<=n;i++){scanf("%lld",&a[i].x);a[i].d=i;}sort(a+1,a+1+n);for (int i=1;i<=n;i++){if (a[i].used==0) a[i].used=++cnt;//没被选过,一定要加 ans[a[i].d]=a[i].used;int p=lower_bound(a+1,a+1+n,(node){a[i].x+d+1,0,0})-a;while (a[p].used&&p<=n) p++;//寻找下一个未操作过的a[p]if (p>n) continue;a[p].used=a[i].used;}printf("%lld\n",cnt);for (int i=1;i<=n;i++) printf("%lld ",ans[i]);return 0;
}
D.Glider
CF原题链接
题目大意:
可恶的翻译为什么不说输入不相交且是升序排列亏得我还想树状数组维护区间
给定高度\(h\)与\(n\)个区间\([l,r]\),保证区间不相交且升序输入。在区间外每过一个单位长度,高度就会下降一个单位长度,在区间内高度不变 请想象原神的风之翼与风场。求能飞的最远距离\((1\leqslant n\leqslant2\times10^{5},1\leqslant h\leqslant 10^{9})\)
解题思路:
贪心地想,优的方案一定是从某个风场区间的左端点开始飞,所以枚举左端点,预处理出下降高度的前缀和,每次二分查找就行。
但是要注意前缀和乱七八糟的细节问题。
不做评价的代码
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
int n,h;
int l[N],r[N],sum[N];
int ans;signed main()
{scanf("%lld%lld",&n,&h);for (int i=1;i<=n;i++){scanf("%lld%lld",&l[i],&r[i]);sum[i]=sum[i-1]+l[i]-r[i-1];//到第i个区间下降总高度的前缀和}for (int i=1;i<=n;i++){int p=lower_bound(sum+1,sum+1+n,sum[i]+h)-sum-1;ans=max(ans,r[p]-l[i]+(h+sum[i]-sum[p]));}printf("%lld",ans);return 0;
}
E.Tree Reconstruction
CF原题链接
题目大意:
给出\(n\),表示有一棵有\(n\)个节点的树,给出每条树边被删除后两个联通块中编号最大的两个结点\(x,y\),要求构造出满足条件的树,输出树边\((2\leqslant n\leqslant 1000)\)
解题思路:
小清新构造题。
可以想到,对于给出节点\(x,y\) 一定存在\(x=n\)或\(y=n\)(因为\(n\)节点一定会是左联通块或右联通块中最大的),若存在一组\(x\neq n\)且\(y\neq n\)那么一定无法构造。
我们可以构造一条链,这样所有\(x\)就相当于这条链上的前缀最大值(我们使\(x\)是另一个联通块的最大值)。将所有\(x\)排序,然后构造就行,无法构造就输出\(NO\)。
为什么无法构造链就一定无法构造出满足要求的树呢?感性理解
虚空调试小技巧:双重循环全是\(i\)
难以描述的代码
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1010;
int n;
int a[N];
bool flag[N];
int ans[N];signed main()
{scanf("%lld",&n);for (int i=1,aa,b;i<n;i++){scanf("%lld%lld",&aa,&b);if (aa>b) swap(aa,b);if (b!=n) { printf("NO"); return 0; }a[i]=aa;}sort(a+1,a+n);for (int i=1;i<=n;i++){if (a[i]!=a[i-1]) {ans[i]=a[i];flag[a[i]]=true;continue;}bool o=false;for (int j=1;j<=a[i];j++){if (!flag[j]) {ans[i]=j;flag[j]=true;o=true;break;}}if (!o) { printf("NO"); return 0; }}ans[n]=n;printf("YES\n");for (int i=1;i<n;i++) printf("%lld %lld\n",ans[i],ans[i+1]);return 0;
}