ARC123

C - 1, 2, 3 - Decomposition

给定 n,找到最小的 k 使得存在 {ak} 满足:

  • ai 之和为 n

  • ai 在十进制下的每个数位均为 1,2,3

T10001n1018


我怎么做出来这个题的?

我们考虑对每个答案 ans 检查 check(n,ans),表示能不能用 ans 个数组出 n

我们来处理 check(x,m)

首先比较显然的:若 x<m,不合法;若 x[m,3m],合法,我们用若干 1,2,3 组合即可。

这之后,然后就会有位数 >1 的数。易知当前最低位的数位之和模 10 一定为 xmod10,我们枚举 i=10k+(xmod10)i[xmod10+10×[xmod10<m],3m](这个范围是显然的),枚举位数 >1 的数的个数 j,检查 check(xi10,j),若存在任意一个合法,check(x,m) 就是合法的。

跑的非常快。实际上,ans 的最大值是 5,这为暴力算法提供了保证。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int read(){
int x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
ll n;
bool check(ll x,int m){
if(x<m)return false;
if(x>=m&&x<=3*m)return true;
bool fl=false;
for(int j=0;j<=m;j++)
for(int i=x%10+(x%10<m?10:0);i<=3*m;i+=10)
if(check((x-i)/10,j))return true;
return false;
}
void solve(){
cin>>n;int ans;
for(ans=1;!check(n,ans);ans++);
printf("%d\n",ans);
}
int main(){
int T;cin>>T;
while(T--)solve();
return 0;
}

D - Inc, Dec - Decomposition

给出 {an},考虑 {bn}{cn} 满足:

  • i[1,n]ai=bi+ci

  • bi 单调不降。

  • ci 单调不升。

试最小化 i=1n(|bi|+|ci|)

n2×105108ai108


贪心结论:若 ai>ai+1,有 bi+1=bi,ci+1=ci+ai+1ai。否则 ci+1=ci,bi+1=bi+ai+1ai

这似乎是十分显然的。

考虑令 b1=x,c1=a1x,接着我们可以将所有 bicix 表示出来。

然后呢?你发现就是一堆点到某个点的距离和的最小值,我们取中位数即可。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define N 200010
using namespace std;
ll read(){
ll x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
int n;ll a[N];
ll B,C,s[N<<1];int m;
int main(){
n=read();
for(int i=1;i<=n;i++)a[i]=read();
B=a[1],C=0;
s[++m]=B,s[++m]=C;
for(int i=1;i<n;i++){
ll x=a[i+1]-a[i];
(x>=0)?B+=x:C-=x;
s[++m]=B,s[++m]=C;
}
sort(s+1,s+1+m);
ll ans=0;
for(int i=1;i<=m;i++)
ans+=abs(s[i]-s[n]);
printf("%lld\n",ans);
return 0;
}

E - Training

给出 n,AX,BX,AY,BY,求:

i=1n[AX+iBX=AY+iBY]

T2×1051n1091AX,BX,AY,BY106


假设 BXBY

f(x)=AX+xBXg(x)=AY+xBYF(x)=f(x)G(x)=g(x),条件即 [F(x)=G(x)]

BX=BY 是简单的。然后我们考虑 f(x)g(x) 这条斜率 >0 的直线。

F(x)=G(x) 显然有 f(x)g(x)(1,1)。那么:

  • f(x)g(x)(1,0]F(x)G(x){1,0}

  • f(x)g(x)(0,1]F(x)G(x){0,1}

我们只考虑第一种。

我们二分出 f(x)g(x)(1,0] 的区间 [l,r],然后你发现求 i=lrF(x)G(x) 就能算出 k 了。这个东西你可以随便写个前缀和,闲的慌也能写类欧。

但是代码实现感觉好难写。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int read(){
int x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
ll f(ll a,ll b,ll c,ll n){
if(n<0)return 0;
if(!a||!n)return (n+1)*(b/c);
ll A=a/c,B=b/c;
if(A||B)return n*(n+1)/2*A+B*(n+1)+f(a%c,b%c,c,n);
ll M=(a*n+b)/c;
return M*n-f(c,c-b-1,a,M-1);
}
ll calc(ll a,ll b,ll c,ll l,ll r){
return f(a,b,c,r)-f(a,b,c,l-1);
}
ll n,a,b,c,d;
void solve(){
n=read(),a=read(),b=read(),c=read(),d=read();
if(b>d)swap(a,c),swap(b,d);
ll l=1,r=n,mid;
ll p1=-1,p2=n+1,p3=-1;
while(l<=r){
mid=(l+r)>>1;
if(mid*(d-b)>b*d*(c-a-1))r=mid-1,p1=mid;
else l=mid+1;
}
if(p1==-1)return puts("0"),void();
l=p1,r=n;
while(l<=r){
mid=(l+r)>>1;
if(mid*(d-b)>b*d*(c-a))r=mid-1,p2=mid;
else l=mid+1;
}
l=p2,r=n;
while(l<=r){
mid=(l+r)>>1;
if(mid*(d-b)<=b*d*(c-a+1))l=mid+1,p3=mid;
else r=mid-1;
}
if(p3==-1)p3=p2-1;
ll ans=0,k=calc(1,c*d,d,p1,p2-1)-calc(1,a*b,b,p1,p2-1);
ans+=p2-p1-k;
k=calc(1,a*b,b,p2,p3)-calc(1,c*d,d,p2,p3);
ans+=p3-p2+1-k;
printf("%lld\n",ans);
}
int main(){
int T=read();
while(T--)solve();
return 0;
}

F - Insert Addition

对于序列 {An},定义序列 f(A)=(A1,A1+A2,A2,A2+A3,,An1+An,An)

给定 nA=(a,b)

然后进行 n 次操作:Af(A)

最后将 A 中所有大于 n 的数删去,记最终数列为 B

给定 L,R,求出 BL,,BR

1a,bn3×1051LRmin(|B|,1018)RL<3×105


我们考虑用系数来刻画这个东西,即用 (x,y) 表示 ax+by。发现初始是 (1,0)(0,1),那么 Af(A) 的过程就是和 Stern-Brocot Tree 是一样的了。

[L,R] 这个区间是这样的:先找到第 L 个,然后再暴力找后面的。在 SB Tree 上搜索,若加上当前子树内的节点数 x 后还未达到 L,直接跳过该子树。

假设已经找到位置 L,继续搜索,由于 SB Tree 树高 O(n),只会经过 O(n) 个无用节点,所以这部分的复杂度是线性的。

考虑 num(a,b) 表示当前子树内的合法节点数,容易有

num(a,b)=i1j1[gcd(i,j)=1][ia+jbn]=d=1nμ(d)i=1ndandaib

整除分块套类欧,这里是 O(nlogn) 的。

在 SB Tree 上跳相同方向的链的时候要倍增,不然会起飞。这样时间复杂度是 O(n+nlog3n) 的。

点击查看代码
#include<bits/stdc++.h>
namespace atcoder {
namespace internal {
// @param m `1 <= m`
// @return x mod m
constexpr long long safe_mod(long long x, long long m) {
x %= m;
if (x < 0) x += m;
return x;
}
// @param n `n < 2^32`
// @param m `1 <= m < 2^32`
// @return sum_{i=0}^{n-1} floor((ai + b) / m) (mod 2^64)
unsigned long long floor_sum_unsigned(unsigned long long n,
unsigned long long m,
unsigned long long a,
unsigned long long b) {
unsigned long long ans = 0;
while (true) {
if (a >= m) {
ans += n * (n - 1) / 2 * (a / m);
a %= m;
}
if (b >= m) {
ans += n * (b / m);
b %= m;
}
unsigned long long y_max = a * n + b;
if (y_max < m) break;
n = (unsigned long long)(y_max / m);
b = (unsigned long long)(y_max % m);
std::swap(m, a);
}
return ans;
}
}
long long floor_sum(long long n, long long m, long long a, long long b) {
assert(0 <= n && n < (1LL << 32));
assert(1 <= m && m < (1LL << 32));
unsigned long long ans = 0;
if (a < 0) {
unsigned long long a2 = internal::safe_mod(a, m);
ans -= 1ULL * n * (n - 1) / 2 * ((a2 - a) / m);
a = a2;
}
if (b < 0) {
unsigned long long b2 = internal::safe_mod(b, m);
ans -= 1ULL * n * ((b2 - b) / m);
b = b2;
}
return ans + internal::floor_sum_unsigned(n, m, a, b);
}
}
#define ll long long
#define N 300010
using namespace std;
ll read(){
ll x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
int a,b,n;ll l,r;
int p[N],pcnt,mu[N];bool vis[N];
void init(){
mu[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i])p[++pcnt]=i,mu[i]=-1;
for(int j=1;j<=pcnt&&i*p[j]<=n;j++){
vis[i*p[j]]=true;
if(i%p[j]==0)break;
mu[i*p[j]]=-mu[i];
}
mu[i]+=mu[i-1];
}
}
ll calc(ll a,ll b){
ll ret=0;
for(int l=1,r;l<=n;l=r+1){
r=n/(n/l);
if(n/l<a+b)break;
ret+=1ll*(mu[r]-mu[l-1])*atcoder::floor_sum(n/l/a,b,-a,n/l-a);
}
return ret;
}
int ans[N],cur;
#define frac pair<ll,ll>
#define fi first
#define se second
frac operator+(frac x,frac y){
return {x.fi+y.fi,x.se+y.se};
}
frac operator*(ll x,frac y){
return {x*y.fi,x*y.se};
}
ll val(frac x){
return 1ll*a*x.fi+1ll*b*x.se;
}
void ins(frac x){
ans[++cur]=val(x);
}
void solve2(frac x,frac y,ll l,ll r){
if(cur>=r-l+1)return;
ll mid=val(x+y);
if(mid>n)return;
solve2(x,x+y,l,r);
if(cur<r-l+1)ins(x+y);
solve2(x+y,y,l,r);
}
void solve1(frac x,frac y,ll l,ll r){
if(cur>=r-l+1)return;
ll a=val(x),b=val(y),mid=val(x+y);
if(mid>n)return;
ll cnt=calc(a,mid)+1;
if(l==cnt){
ins(x+y);
solve2(x+y,y,l,r);
return;
}
if(l<cnt){
int dep=0;
for(int i=20;~i;i--){
if(l<calc(a,val((dep+(1<<i))*x+y))+1)
dep+=(1<<i);
}
solve1(x,dep*x+y,l,r);
for(int i=dep;i;i--){
if(cur>=r-l+1)break;
ins(i*x+y);
solve2(i*x+y,(i-1)*x+y,l,r);
}
}
else{
int dep=0;ll tot=calc(a,b);
for(int i=20;~i;i--){
if(tot-l+1<calc(val(x+(dep+(1<<i))*y),b)+1)
dep+=(1<<i);
}
cnt=tot-calc(val(x+dep*y),b);
solve1(x+dep*y,y,l-cnt,r-cnt);
}
}
int main(){
a=read(),b=read(),n=read(),l=read(),r=read();
init();
ll cnt=calc(a,b);
if(l!=cnt+2&&r!=1)
solve1({1,0},{0,1},max(l-1,1ll),min(r-1,cnt));
if(l==1)printf("%d ",a);
for(int i=1;i<=cur;i++)printf("%d ",ans[i]);
if(r==cnt+2)printf("%d ",b);
printf("\n");
return 0;
}

本文作者:SError

本文链接:https://www.cnblogs.com/SError0819/p/18077235

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   SError  阅读(12)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起