ARC123

SError發表於2024-03-16

C - 1, 2, 3 - Decomposition

給定 \(n\),找到最小的 \(k\) 使得存在 \(\{a_k\}\) 滿足:

  • \(a_i\) 之和為 \(n\)

  • \(a_i\) 在十進位制下的每個數位均為 \(1,2,3\)

\(T\le 1000\)\(1\le n\le 10^{18}\)


我怎麼做出來這個題的?

我們考慮對每個答案 \(ans\) 檢查 \(\operatorname{check}(n,ans)\),表示能不能用 \(ans\) 個陣列出 \(n\)

我們來處理 \(\operatorname{check}(x,m)\)

首先比較顯然的:若 \(x<m\),不合法;若 \(x\in[m,3m]\),合法,我們用若干 \(1,2,3\) 組合即可。

這之後,然後就會有位數 \(>1\) 的數。易知當前最低位的數位之和模 \(10\) 一定為 \(x\bmod 10\),我們列舉 \(i=10k+(x\bmod 10)\)\(i\in[x\bmod 10+10\times[x\bmod 10<m],3m]\)(這個範圍是顯然的),列舉位數 \(>1\) 的數的個數 \(j\),檢查 \(\operatorname{check}(\dfrac{x-i}{10},j)\),若存在任意一個合法,\(\operatorname{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

給出 \(\{a_n\}\),考慮 \(\{b_n\}\)\(\{c_n\}\) 滿足:

  • \(\forall i\in[1,n]\)\(a_i=b_i+c_i\)

  • \(b_i\) 單調不降。

  • \(c_i\) 單調不升。

試最小化 \(\sum_{i=1}^{n} (|b_i|+|c_i|)\)

\(n\le 2\times 10^5\)\(-10^8\le a_i\le 10^8\)


貪心結論:若 \(a_i>a_{i+1}\),有 \(b_{i+1}=b_i,c_{i+1}=c_i+a_{i+1}-a_i\)。否則 \(c_{i+1}=c_i,b_{i+1}=b_i+a_{i+1}-a_i\)

這似乎是十分顯然的。

考慮令 \(b_1=x,c_1=a_1-x\),接著我們可以將所有 \(b_i\)\(c_i\)\(x\) 表示出來。

然後呢?你發現就是一堆點到某個點的距離和的最小值,我們取中位數即可。

點選檢視程式碼
#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,A_X,B_X,A_Y,B_Y\),求:

\[\sum_{i=1}^{n}[A_X+\lfloor\frac{i}{B_X}\rfloor=A_Y+\lfloor\frac{i}{B_Y}\rfloor] \]

\(T\le 2\times 10^5\)\(1\le n\le 10^9\)\(1\le A_X,B_X,A_Y,B_Y\le 10^6\)


假設 \(B_X\le B_Y\)

\(f(x)=A_X+\frac{x}{B_X}\)\(g(x)=A_Y+\frac{x}{B_Y}\)\(F(x)=\lfloor f(x)\rfloor\)\(G(x)=\lfloor g(x)\rfloor\),條件即 \([F(x)=G(x)]\)

\(B_X=B_Y\) 是簡單的。然後我們考慮 \(f(x)-g(x)\) 這條斜率 \(>0\) 的直線。

\(F(x)=G(x)\) 顯然有 \(f(x)-g(x)\in (-1,1)\)。那麼:

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

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

我們只考慮第一種。

我們二分出 \(f(x)-g(x)\in(-1,0]\) 的區間 \([l,r]\),然後你發現求 \(\displaystyle \sum_{i=l}^{r}F(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;
}