[AGC057D] Sum Avoidance

考虑到 \((S-i,i)\) 不会同时出现。

\(\forall x \geq (S+1)/2\) 是一组合法的构造方案,则 A 大小为 \((S-1)/2\)

接下来考虑对 \(\leq (S-1)/2\) 的集合进行选择,设他们构成了集合 B,可以列出若干条件:

  • \(a\in B,b \in B,a+b\leq (S-1)/2\),则 \(a+b \in B\)
  • \(B\) 不能凑出 \(S\)

不难发现这是充要条件。

考虑找到第一个加入的数,设为 \(d\),最大的时候也就 \(43\)

考虑从小到大加入。

  • 能被凑出,必须加入
  • 不能被凑出,加入不会造成影响,加入。

考虑同余最短路,此时很容易发现第二种加入不超过 d 种。

同时发现,如果加入了第一种,对我们的 \(f\) 数组没有任何影响,所以我们先考虑算出 f 数组,然后计算对应的第 \(k\) 个。

由于是从小到大进行贪心,我们寻找下一个不能被凑出的,加入后不能凑出 \(S\) 的,最小的数。

假设加入的数为 \(v\),记 \(x=v \mod d\)

  • 不能凑出,对应 \(v\lt f_x\)
  • 不能凑出 \(S\),也就是更新后 \(f_{S \mod d}\gt S\),而加入后的转移柿为 \(f_{y}+iv \rightarrow f_{(y+ix)\mod d}\)

我们枚举对应的 \(x\),满足其对应的 \(f_x=\inf\),并求出其对应的最小 \(v\),对于每个 \(i\) 都有要求,也就对应了:

  • \(f_{(S-ix)\mod d}+iv\gt S\),也就是 \(v\geq\lfloor \frac{S-f_{(S-ix)\mod d}}{i}\rfloor +1\)

找到所有 \(x\) 中最小的 \(v\),满足其小于等于 \((S-1)/2\),对应更新。

最后我们还要求出第 \(k\) 个,我们考虑二分 \(x\),最后我们能凑出来的,且小于 \((S-1)/2\) 的一定都在 \(B\) 里面,所以柿子为 \(\sum \limits_{i=0}^{d-1}(x-f_i)/d+[i\neq 0]\)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int ll
const ll inf=1e18+10; 
int t,d;
ll S,K,f[50];
ll gcd(ll a,ll b){return !b?a:gcd(b,a%b);}
inline ll calc(ll x){
	ll ans=0;
	for(int i=0;i<d;i++)
		if(f[i]<=x)ans+=(x-f[i])/d+1;
	return ans-1;
}
signed main(){
	scanf("%lld",&t);
	while(t--){
		scanf("%lld%lld",&S,&K); 
		if(K>(S-1)/2){puts("-1");continue;}
		for(d=1;S%d==0;d++);
		for(int i=1;i<d;i++)f[i]=inf;f[0]=0;
		while(1){
			int mv=inf;
	        for(int x=1;x<d;x++)
	        {
	            int v=0;
	            for(int i=1;i<d;i++) v=max(v,(S-f[((S-x*i)%d+d)%d])/i+1);
	            while(v%d!=x) v++;
	            if(v>=f[x]) continue;
	            mv=min(mv,v);
	        }
	        if(mv>(S-1)/2) break;
	        f[mv%d]=mv;
	        for(int i=0,gd=__gcd(mv,d);i<gd;i++)
	        {
	            for(int t=i,c=0;c<2;c+=(t==i))
	            {
	                int nxt=(t+mv)%d;
	                f[nxt]=min(f[nxt],f[t]+mv),t=nxt;
	            }
	        }
		}
		if(calc((S-1)/2)>=K){
			ll l=1,r=(S-1)/2;
			while(l<r){
				ll mid=(l+r)>>1;
				if(calc(mid)<K)l=mid+1;
				else r=mid;
			}
			printf("%lld\n",l);
		}else{
			ll l=(S+1)/2,r=S-1;
			while(l<r){
				ll mid=(l+r)>>1;
				if((S-1)/2-(S-1-mid-calc(S-1-mid))<K)l=mid+1;
				else r=mid;
			}	
			printf("%lld\n",l);
		}
		
	}
}
posted @ 2024-08-01 14:46  xcyyyyyy  阅读(13)  评论(0编辑  收藏  举报