Codeforces Round #813(Div.2) 题解
ABC 送分题,B的证明好像有点有趣,不过结论猜就完了
A
// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Madoka"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long LL;
const int inf = 1e9;
int n,k;
int a[105];
void solve(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
int p = k, cnt=0;
for(int i=1;i<=k;i++){
if(a[i] > p)++ cnt;
}
printf("%d\n",cnt);
}
signed main(){
int te;scanf("%d",&te);
while(te --)solve();
return 0;
}
B
// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Madoka"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long LL;
const int inf = 1e9;
void solve(){
int n;
scanf("%d",&n);
if(n%2==0){
for(int i=1;i<=n;i+=2)printf("%d %d ",i+1,i);
puts("");
}else{
printf("1 ");
for(int i=2;i<=n;i+=2)printf("%d %d ",i+1,i);
puts("");
}
}
signed main(){
int te;scanf("%d",&te);
while(te--)solve();
return 0;
}
C
// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Madoka"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long LL;
const int inf = 1e9;
int n;
int a[100005],lt[100005],vis[100005],mx[100005];
void solve(){
memset(lt,0,sizeof lt);
memset(vis,0,sizeof vis);
memset(mx,0,sizeof mx);
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
int lst=-1;
for(int i=n;i>=2;i--){
if(a[i] < a[i-1]){lst = i-1;break;}
}
for(int i=1;i<=n;i++)lt[a[i]] = i;
for(int i=1;i<=n;i++)mx[i] = max(mx[i-1],lt[a[i]]);
if(lst == -1){puts("0");return ;}
for(int i=lst;i<=n;i++){
if(lt[a[i]]!=i)continue;
if(mx[i] == i){
int cnt=0;
for(int j=1;j<=i;j++){
if(!vis[a[j]])++cnt,vis[a[j]]=1;
}
printf("%d\n",cnt);
return ;
}
}
}
signed main(){
int te;scanf("%d",&te);
while(te --)solve();
return 0;
}
D
首先注意到对于一个给定序列,答案就是
解释:首先我们要最大化的就是某两点最短路的最大值。考虑最小值的位置,如果位于之间,那么根据定义,与之间连一条长为最小值的边,易知这一定是l和r的最短路长度,显然不是我们想要的,因为我们要的是最短路的最大值。如果,我们考虑l和r的最短路长什么样,考虑两种情况: 显然我们取能包含住的就能取到最小值,即为,显然这是这种情况最小值,第二种情况是直接,长度也很显然,是,最短距离即为二者最小值
现在我们想要最大化这个最小值,怎么做?显然第一种情况不可能再小了,只能第二种情况变大,注意到,也就是说我们应该尽量缩小l r距离,显然当可以。也就是得到了上式
如何利用好赋值?普通的贪心是会有反例的,但是我们发现,对于某一个答案来说,如果他能够通过次赋值得到,那么比这个答案更小的一定也能不多于次赋值得到,可以二分。
于是二分答案,判断修改次数是否小于即可
(考场尝试了3次贪心都错了...)
// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Madoka"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long LL;
const int inf = 1e9,maxn=100005;
int n,k;
int a[maxn],b[maxn];
int check(int x){
for(int i=1;i<=n;i++)b[i]=a[i];
int tot=0;
for(int i=1;i<=n;i++)
if(a[i]*2 < x)++ tot, b[i] = 1e9;
int res=2;
for(int i=1;i<n;i++){
res = min(res, (b[i]<x) + (b[i+1]<x));
}
return res+tot <= k;
}
void solve(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
int l=1,r=1e9,mid,ans;
while(l <= r){
mid = l+r>>1;
if(check(mid))l = mid+1,ans=mid;
else r = mid-1;
}
printf("%d\n",ans);
}
int main(){
int te;scanf("%d",&te);
while(te --)solve();
return 0;
}
E1&E2
容斥一下,就转化为了求的方案
转化的目的是为了放缩
即,又因为是的倍数,必然有 或者 (i+j>k是由2k<i+j+k得到)
先考虑第一种情况,显然和都是的约数,对于每个我们可以暴力枚举,统计在的的约数个数,记为,则其对答案的贡献即为
第二种情况?首先和都是的约数,易知,所以只可能取(如果为整数),将代回不等式反解,,可取,如果为整数,对于每个额外判断一下即可
求出答案之后即可
时间复杂度,可以过E1
对于,考虑离线,按照从大到小排序
维护一个指针,从大到小扫描,对于某个位置,考虑(相当于E1我是枚举因数,E2我是枚举倍数,对于每个因数只需要枚举一次)每次枚举到某个倍数,第一次+0,第二次+1,...这样就相当于统计了E1中的(为什么第二次才+1?因为相当于枚举的是,而枚举到的次数就相当于在[l..k)间的因子个数cnt,第二次枚举到相当于有2个这种因数,)
查询的时候就相当于的区间和,因为我从大到小的,所以不存在不合法情况
树状数组维护即可
考虑对于第二种情况的影响,对于的形式,必然为15的倍数,有约束,对于,k是15的倍数,k的个数为,根据这个统计答案,对于同理
E1
// by Balloons
#include <cstdio>
#include <cstring>
#include <vector>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Madoka"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long LL;
const int inf = 1e9,maxn=2e5+5;
vector<int>divsor[maxn];
void solve(){
int l,r;scanf("%d%d",&l,&r);
LL res=0;
for(int i=l+2;i<=r;i++){
int k = i,cnt=0;
for(int tp=0;tp<divsor[k].size();tp++){
int cur = divsor[k][tp];
if(l<=cur && cur<k)++ cnt;
}
res += 1ll*cnt*(cnt-1)/2;
if(k%15==0 && 2*k/5 >=l)++ res;
if(k%6 == 0 && k/2 >=l) ++ res;
}
int len = r-l+1;
LL ans = 1ll*len*(len-1)*(len-2) / 6;
printf("%I64d\n",ans - res);
}
signed main(){
for(int i=1;i<=200000;i++){
for(int j=i;j<=200000;j+=i){
divsor[j].push_back(i);
}
}
int te;scanf("%d",&te);
while(te--)solve();
return 0;
}
E2
// by Balloons
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Madoka"<<endl
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
typedef long long LL;
const int inf = 1e9,maxn=2e5+5;
struct node{int l,r,id;}a[maxn];
int cmp(node a,node b){return a.l > b.l;}
int buc[maxn],bit[maxn];
LL qu[maxn];
int lowbit(int x){return x&(-x);}
void add(int x,int tp){for(int i=x;i<=200000;i+=lowbit(i))bit[i] += tp;}
LL query(int x){LL res=0;for(int i=x;i;i-=lowbit(i))res += bit[i];return res;}
signed main(){
int T;scanf("%d",&T);
for(int i=1;i<=T;i++){
scanf("%d%d",&a[i].l,&a[i].r);
a[i].id = i;
}
sort(a+1,a+T+1,cmp);
int curid = 200001;
for(int i=1;i<=T;i++){
int lx = a[i].l, ly = a[i].r, lid = a[i].id;
while(curid>lx){
-- curid;
for(int t=curid*2;t<=200000;t+=curid){
add(t,buc[t]);
++ buc[t];
}
}
int len = ly-lx+1;
LL ans = 1ll*len*(len-1)*(len-2)/6;
ans -= query(ly);
ans -= max(0, ly/15 - (lx-1) / 6);
ans -= max(0ll, ly/6 - 1ll*(lx-1) / 3);
qu[lid] = ans;
}
for(int i=1;i<=T;i++)printf("%I64d\n",qu[i]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下