CF #643(div.2)

A. Sequence with Digits

time limit per test: 1 second
memory limit per test: 256 megabytes

题意: 给定如下递推式
a n + 1 = a n + m i n D i g i t ( a n ) ⋅ m a x D i g i t ( a n ) . a_{n+1}=a_n+minDigit(a_n) \cdot maxDigit(a_n). an+1=an+minDigit(an)maxDigit(an).
其中 m i n D i g i t ( a n ) minDigit(a_n) minDigit(an) m a x D i g i t ( a n ) maxDigit(a_n) maxDigit(an) 分别代表 a n a_n an 十进制表示中 最小 和 最大 的数字,给出 a 1 a_1 a1 k k k ,让你求 a k a_k ak

t   ( 1 ≤ t ≤ 1000 ) t~(1 \leq t \leq 1000) t (1t1000) 组测试数据,每组数据给出 a 1 a_1 a1 k   ( 1 ≤ a 1 ≤ 1 0 18 , 1 ≤ k ≤ 1 0 16 ) k~(1 \leq a_1 \leq 10^{18},1 \leq k \leq 10^{16}) k (1a11018,1k1016)

分析: 只要第一次 m i n D i g i t ( a i ) = 0 minDigit(a_i)=0 minDigit(ai)=0 出现,那么 a i , a i + 1 , . . . a_i,a_{i+1},... ai,ai+1,... 的值都不会变,所以按照公式递推直到算出 a k a_k ak 或者出现 m i n D i g i t ( a i ) = 0 minDigit(a_i)=0 minDigit(ai)=0 终止就好了 (关于 0 0 0 在有限且不高次内一定会出现就不证明了,也不咋会证)

代码:

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define ll long long

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	ll t,n,k;
	cin>>t;
	while(t--)
	{
		cin>>n>>k;
		if(k==1){
			cout<<n<<endl;
			continue;
		}
		k--;
		while(k--)
		{
			ll tmp=n;
			ll Min=9,Max=0;
			while(tmp)
			{
				ll res=tmp%10;
				tmp/=10;
				Min=min(Min,res);
				Max=max(Max,res);
			}
			if(Min==0) break;
			else n+=Min*Max;
		}
		cout<<n<<endl;
	}
} 

B. Young Explorers

time limit per test: 2 seconds
memory limit per test: 256 megabytes

题意: T   ( 1 ≤ T ≤ 2 ⋅ 1 0 5 ) T~(1 \leq T \leq 2 \cdot 10^5) T (1T2105) 组测试数据,每组给出 N   ( 1 ≤ N ≤ 2 ⋅ 1 0 5 ) N~(1 \leq N \leq 2 \cdot 10^5) N (1N2105) 和大小为 N N N 的数组 e   ( 1 ≤ e i ≤ N ) e~(1 \leq e_i \leq N) e (1eiN)

∑ N ≤ 3 ⋅ 1 0 5 \sum N \leq 3 \cdot 10^5 N3105

对于每组数据,你需要把数组 e e e 分成尽可能多的组(不必所有的元素都必须分配),但是每组内的元素需要满足 组的大小大于等于组内任意一个元素,即对于一个大小为 m m m 的组 b b b ,需要满足 b i ≤ m b_i \leq m bim

求最多可以分成多少组?

分析: 贪心,把数组 e e e 排序 然后从小到大尽量分组划分;

代码:

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
const int N = 3E5+10;
int a[N];

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t,n;
	cin>>t;
	while(t--)
	{
		cin>>n;
		rep(i,1,n) cin>>a[i];
		sort(a+1,a+n+1);
		int ans=0,xb=1;
		while(xb<=n&&xb+a[xb]-1<=n){
			if(a[xb]==a[xb+a[xb]-1]) ans++,xb+=a[xb];
			else{
				int num=a[xb];
				xb+=a[xb]-1;
				while(xb+1<=n&&num<a[xb]) num++,xb++;
				if(num==a[xb]) ans++,xb++;
				else break;
			}
		}
		cout<<ans<<endl;
	}
} 

C. Count Triangles

time limit per test: 1 second
memory limit per test: 256 megabytes

题意: 给定 A , B , C , D   ( 1 ≤ A ≤ B ≤ C ≤ D ≤ 5 ⋅ 1 0 5 ) A,B,C,D~(1 \leq A \leq B \leq C \leq D \leq 5 \cdot 10^5) A,B,C,D (1ABCD5105) ,求有多少组 ( x , y , z ) (x,y,z) (x,y,z) 满足 A ≤ x ≤ B ≤ y ≤ C ≤ z ≤ D A \leq x \leq B \leq y \leq C \leq z \leq D AxByCzD 且构成非退化三角形;

分析: 因为 x ≤ y ≤ z x \leq y \leq z xyz ,差分一下 x + y x+y x+y 的出现次数 然后求一遍前缀和 再枚举 z z z

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
const int N = 1E6+10;

int A,B,C,D;
ll num[N];

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	cin>>A>>B>>C>>D;
	rep(i,A,B)
	{
		num[i+B]++;
		num[i+C+1]--;
	}
	rep(i,1,N-1) num[i]+=num[i-1];  //第一遍求出num[i]表示x+y==i有几种
	rep(i,1,N-1) num[i]+=num[i-1];  //第二遍求前缀和
	ll ans=0;
	rep(i,C,D) ans+=num[N-1]-num[i];
	cout<<ans; 
}

D. Game With Array

time limit per test: 1 second
memory limit per test: 256 megabytes

题意: 给定 N N N S   ( 1 ≤ N ≤ S ≤ 1 0 6 ) S~(1 \leq N \leq S \leq 10^6) S (1NS106) ,现在要你构造出一个大小为 N N N 的数组 s   ( 0 < s i ≤ S ) s~(0<s_i \leq S) s (0<siS) 然后选择一个数 K   ( 0 ≤ K ≤ S ) K~(0 \leq K \leq S) K (0KS),在数组满足 ∑ s i = S \sum s_i=S si=S 的情况下,无法在数组中选取若干元素的和 s u m sum sum 使得 s u m = K    o r    s u m = S − K sum=K~~or~~sum=S-K sum=K  or  sum=SK

分析: 构造方法不只一种,但是 S < 2 ∗ N S<2*N S<2N 肯定无解,然后考虑 K = 1 K=1 K=1 的话,构造出 2 , 2... , S − 2 ∗ ( N − 1 ) 2,2...,S-2*(N-1) 2,2...,S2(N1) 这类形式的序列,就可以满足题意

代码:

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int n,s;
	cin>>n>>s;

	if(2*n>s)
	{
		cout<<"NO";
		return 0;
	}
	cout<<"YES\n";
	for(int i=1;i<n;i++) cout<<2<<' ';
	cout<<s-2*(n-1)<<endl;
	cout<<1; 
} 

E. Restorer Distance

time limit per test: 1 second
memory limit per test: 256 megabytes

题意: 先给出 N , A , R , M   ( 1 ≤ N ≤ 1 0 5 , 0 ≤ A , R , M ≤ 1 0 4 ) N,A,R,M~(1 \leq N \leq 10^5,0 \leq A,R,M \leq 10^4) N,A,R,M (1N105,0A,R,M104) ,然后给出大小为 N N N 的数组 h   ( 0 ≤ h i ≤ 1 0 9 ) h~(0 \leq h_i \leq 10^9) h (0hi109) 分别代表 N N N 根砖柱的高度(1单位高度代表一块砖),现在有三种操作

  • 花费代价 A ,在某一根砖柱上加一块砖
  • 花费代价 R ,从某一根砖柱上拿走一块砖
  • 花费代价 M ,从某一根砖柱上拿一块砖放到另一根砖柱上

现在要求用最小的代价使得所有砖柱高度一致

分析: 三分

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
const int MAXN = 1E5+10; 

int N,A,R,M; 
ll a[MAXN],sum[MAXN];

ll cal(ll H)
{
	int x=1;
	while(x<=N&&a[x]<H) x++;
	ll left=0;
	if(x<=N) left=sum[N]-sum[x-1]-H*(N-x+1);
	ll need=0;
	if(x>1) need=H*(x-1)-sum[x-1];

	ll cost=0;
	if(A+R>=M){
	    if(left>=need) cost+=need*M,left-=need,need=0;
	    else cost+=left*M,need-=left,left=0; 
	}
	cost+=left*R;
	cost+=need*A;
	return cost;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	cin>>N>>A>>R>>M;
	rep(i,1,N) cin>>a[i];
	sort(a+1,a+N+1);
	rep(i,1,N) sum[i]=sum[i-1]+a[i];
	ll L=0,R=1E9+1;
	while(L+1<R)
	{
		ll m1=(L+R)>>1;
		ll m2=(m1+R)>>1;
		if(cal(m1)<cal(m2)) R=m2;
		else L=m1;
	} 
	ll ANS=cal(L);
	cout<<ANS;
} 

F. Guess Divisors Count

time limit per test: 2 seconds
memory limit per test: 256 megabytes

题意: 交互题,预先设定一个数 X   ( 1 ≤ X ≤ 1 0 9 ) X~(1 \leq X \leq 10^9) X (1X109) ,让你通过交互得出 X X X 有多少个因数,你有至多 22 次询问,每次询问形式 ? Q ( 1 ≤ Q ≤ 1 0 18 ) (1 \leq Q \leq 10^{18}) (1Q1018) ,然后会反馈 GCD(X,Q) ,即最大公约数;设 X X X 实际有 d d d 个因数,你给出的答案为 a n s ans ans ,那么只要

  1. ∣ a n s − d ∣ ≤ 7 |ans-d| \leq 7 ansd7
  2. 1 2 ≤ a n s d ≤ 2 \frac{1}{2} \leq \frac{ans}{d} \leq 2 21dans2

至少满足上述两个条件之一,就判正确

分析: 肯定会先想到素数分解的形式: X = p 1 a 1 ⋅ p 2 a 2 ⋯ p m a m X=p_1^{a_1} \cdot p_2^{a_2} \cdots p_m^{a_m} X=p1a1p2a2pmam ,那么 d = ( a 1 + 1 ) ∗ ( a 2 + 1 ) ⋯ ( a m + 1 ) d=(a_1+1)*(a_2+1) \cdots (a_m+1) d=(a1+1)(a2+1)(am+1) ;然后明确一点,题目给定了误差限制,所以可以推测不可能在 22 次询问里求得准确答案,即不是 X 的每个素因子都能询问到,可以从这一点考虑:

  • 只有 22 次询问机会, ( 1 ≤ X ≤ 1 0 9 ) (1 \leq X \leq 10^9) (1X109) ( 1 ≤ Q ≤ 1 0 18 ) (1 \leq Q \leq 10^{18}) (1Q1018),所以,1 次询问考虑询问多个素数的乘积;
  • 考虑多个素数的乘积 ( 1 ≤ Q ≤ 1 0 18 ) (1 \leq Q \leq 10^{18}) (1Q1018) ,其中若有较小的素数,那么它的次数肯定不能是 1 ,否则若 X X X 中关于这个素数的次数很高那么这个素数的询问完全是没有用的,所以得提高次数;然后,最小的素数是 2 且 2 30 > 1 0 9 ≥ X 2^{30}>10^9 \geq X 230>109X ,所以可以 以 2 10 = 1024 2^{10}=1024 210=1024 为上界,提高乘积内每个素数的次数,这样一个 Q 内大概会有 5,6 个素数,22 次询问从小到大 大概可以询问到 八百多的素数;极端情况下,我们计算的某个素数的次数可能只统计了刚好超过 1 3 \frac{1}{3} 31 ,例如 X = 2 29 X=2^{29} X=229;所以得出最后的结果再乘以 2 ,即 ans*2,那么对于已经计算过的素数误差完全是在条件2范围内的,而对于之后的素数出现的情况,分情况考虑:
  • 若之后的素数出现了 3 次,那么前面的素数一个都不会出现,因为 ( 1 ≤ X ≤ 1 0 9 ) (1 \leq X \leq 10^9) (1X109),标准因数个数最大为 d = 2 × 2 × 2 = 8 d=2 \times 2 \times 2=8 d=2×2×2=8,而我们 ans 初始是1,最后乘以2, ∣ a n s − d ∣ ≤ 7 |ans-d| \leq 7 ansd7 成立,满足条件 1;
  • 若之后的素数出现了 2 次,那么前面的素数的所有出现次数完全被计算了(因为 1 ≤ X ≤ 1 0 9 1 \leq X \leq 10^9 1X109),除开后面的两个素数之后,X 最多只剩下 1000 左右;此时后面的素数 d 的影响最多是 2 × 2 = 4 2 \times 2=4 2×2=4 倍级别,所以 ans*2 之后是满足条件 2 的;
  • 若之后的素数出现了 1 次,极端情况下,前面的素数最多被少算了 1 倍 ,例如 X = 2 20 × 863 X=2^{20} \times 863 X=220×863,此时 ans*2 之后依旧满足条件 2;

代码:

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define ll unsigned long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
const int N = 1000;
vector<ll>pm;  //素数
vector<ll>pp;  //提高次数的素数
vector<ll>test; //多个提高次数的素数的乘积

void ready()
{
	bool vis[N]={0};
	rep(i,2,N-1)if(!vis[i]){
		pm.pb(i);
		for(int j=i;j<N;j+=i) vis[j]=1;
	}
	for(auto v:pm)
	{
		ll x=v;
		while(x*v<=1024)x*=v;
		pp.pb(x);
	}
	ll res=1;
	int cnt=0;
	for(auto v:pp)
	{
		if(res*v>1e18) test.pb(res),res=v,++cnt;
		else res*=v;
		if(cnt==22) {cout<<"---"<<v<<endl;break;} 
	}
	if(res>1) test.pb(res);
	
}

ll ask(ll m)
{
	cout<<"? "<<m<<endl;
	ll res;cin>>res;
	return res;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
    ready();
    for(auto v:pm) cout<<v<<endl; 
    int t;
    cin>>t;
    while(t--)
    {
    	ll ANS=1;
    	rep(k,0,21)
    	{
    		ll ans=ask(test[k]),cnt;
    		for(auto v:pm){
    			cnt=0;
    			while(ans%v==0) cnt++,ans/=v; 
    			ANS*=(cnt+1);
			}
		}
		cout<<"! "<<ANS*2<<endl;
	}
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值