2024牛客8

a.Haitang and Game

题意:
给定一个集合,dXqwq和Haitang轮流在选集合选x,y,假如gcd(x,y)不在集合内,可以插入gcd(x,y)。
不能操作者输。

题解:
注意数据范围小,只有1e5. 枚举每个数x的倍数,假如这个数存在的倍数的gcd等于x,那这个数就可以进行一次操作。
我们就知道一共可以操作多少次,结果就清楚了

代码:

#include<bits/stdc++.h>
using namespace std;
typedef __int128 i128;
typedef long long ll;
typedef double db;

const db PI = acos(-1);
typedef array<ll, 2> PII; // vector<PII> a(n + 1);
const ll inf = 2e18 + 10;
const int mod = 998244353;
const int maxn = 1e5 + 10;
bool multi = 1;

void Solve() {
    ll n; cin >> n;
    vector<ll> a(maxn + 10);
    for(ll i = 1; i <= n; i ++ ) {
        ll x; cin >> x;
        a[x] = 1;
    }
    ll cnt = 0;
    for(ll i = maxn; i >= 1; i -- ) {
        ll x = 0;
        if(a[i] == 1) continue;
        for(ll j = 1; j * i <= maxn; j ++ ) {
            if(a[j * i]) {
               if(x == 0) x = j * i;
               else x = __gcd(x, j * i); 
            }
        }
        if(x == i) {
            cnt ++ ;
            a[i] = 1;
        }
    }
    if(cnt % 2 == 1) cout << "dXqwq\n";
    else cout << "Haitang\n";
}


signed main() {
    // freopen("test.in","r",stdin);  
    // freopen("code.out","w",stdout);    
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    ll T = 1;
    if(multi) cin >> T;
    while(T -- ) {
        Solve();
    }
    return 0;
}

E.Haitang and Math

题意:
定义S(m)的值是m每一位上数字的和。
给定一个数字n,问有多少m<=n满足n%m=S(m).
1 < = n < = 1 0 12 1<=n<=10^{12} 1<=n<=1012

题解:
n % m = S ( m ) n\% m=S(m) n%m=S(m) 等价于 n − S ( m ) = k × m n-S(m)=k \times m nS(m)=k×m
S(m)的范围就是0~108,可以找 n − S ( m ) n-S(m) nS(m)的因数,判断 m < = n m<=n m<=n.
现在问题就是找到 [ n − 108 , n ] [n-108,n] [n108,n]每个数的因数,枚举因数m判断是否满足条件。
但是n的范围很大,不能用用根号n的时间复杂度。我们使用区间筛法,

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+6;
const int M=108;
bool a[N] = { 1,1 };
int b[N],ans;
int v[500];
vector<pair<int,int>> e[500];
pair<int,int>su;

int sum(int x)
{
	int re=0;
	while(x){
		re+=x%10;
		x/=10;
	}
	return re;
}
void prime()
{
    int cnt = 0;
    for (int i = 2; i <= N; i++)
    {
        if (a[i] == 0)    b[++cnt] = i;
        for (int j = 1; j <= cnt; j++)
        {
            if (i * b[j] > N)break;
            a[i * b[j]] = 1;
            if (i % b[j] == 0)break;
        }
    }
    
}
#define pt vector<pair<int,int>>::iterator
void dfs(int x,pt s,pt t,int req)
{
	if(s==t)
	{//特判x!=req,因为要满足n%m<S(m)
		if(sum(x)==req) ans+=(x!=req);
		return ;
	}
	su=*s;
	int y=su.first,z=su.second;
	++s,dfs(x,s,t,req);
	for(int i=1; i<=z; ++i)
		x*=y,dfs(x,s,t,req);
	return ;
}
void solve()
{
	int n,k;
	cin>>n;
	if(n<=M){
		ans=0;
		for(int i=1;i<=n;i++){
			if(n%i==sum(i))ans++;
		}
		cout<<ans<<'\n';
		return ;
	}
	for(int i=0;i<=M;i++){
		v[i]=n-i;
		e[i].clear();
	}
	
	int cnt;
	for(int i=1;1ll*b[i]*b[i]<=n;i++){
		if(b[i]<=M){
			for(int j=0;j<=M;j++){
				cnt=0;
				while(v[j]%b[i]==0){
					cnt++;
					v[j]/=b[i];
				}
				if(cnt){
					e[j].push_back({b[i],cnt});
				}
			}
			
		}else if(n%b[i]<=M){
			int j=n%b[i];
			cnt=0;
			while(v[j]%b[i]==0){
				cnt++;
				v[j]/=b[i];
			}
			if(cnt){
				e[j].push_back({b[i],cnt});
			}
		}
	}
	for(int i=0;i<=M;i++){
		if(v[i]>1){
			e[i].push_back({v[i],1});
		}
	}
	ans=0;
	for(int i=0;i<=M;i++){
		dfs(1,e[i].begin(),e[i].end(),i);
	}
	cout<<ans<<'\n';
}

signed main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(nullptr);
//	cout.tie(nullptr);
	prime();
	int T = 1;
	cin >> T;
	while (T--) {
		solve();
	}
}

J.Haitang and Triangle

题意:
给定序列长度 n n n,构造长度为 n n n的的序列满足条件:
m m m个长度为3的子序列的数字可以构成一个三角形。

题解:
长度为 n n n的序列有n-2个长度为3的子序列。

1 1 1和任意另外两个数都不能构成三角形,所以 m = n − 2 m=n-2 m=n2时不存在这样的序列。

序列是 [ 1 , 2 , 3 … … n ] [1,2,3……n] [1,2,3……n]时有m-3个子序列满足条件。

d = ( n / 3 ) d=(n/3) d=(n/3)
序列是 [ d , 2 d , 3 d , d − 1 , 2 d − 1 , 3 d − 1 … … , 1 , 1 + d , 1 + 2 d ] [d,2d,3d,d-1,2d-1,3d-1……,1,1+d,1+2d] [d,2d,3d,d1,2d1,3d1……,1,1+d,1+2d]时有0个子序列满足条件。

m是其他值时,可以可以搭配这两种序列排法,前n-m个数不应该有三角形子序列,用方法二构造,后 m m m个数用方法一构造,就刚好有 m m m个子序列可以构造出三角形。
现在考虑 ( n − m ) m o d    3 (n-m) \mod 3 (nm)mod3 有余数情况:
( n − m ) m o d    3 = 1 (n-m) \mod 3=1 (nm)mod3=1,在前面插入 n n n.
( n − m ) m o d    3 = 2 (n-m) \mod 3=2 (nm)mod3=2,在前面插入 3 d + 1 3d+1 3d+1.后面插入 3 d + 2 3d+2 3d+2
剩下的数顺序放就行。

代码:

#include <bits/stdc++.h>
#define int long long
#define rep(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
const int N=3e5+6;
int a[N],n,m;

void solve()
{
	cin>>n>>m;
	if(n==m+2){
		cout<<"-1\n";
		return ;
	}
	

	int d=(n-m)/3;
	int t=1,x=3*d;	
	if((n-m)%3==1){
		a[t++]=n;

		x=3*d;
	}
	
	if((n-m)%3==2){
		a[t++]=3*d+1;
	}
	
	int s1=d,s2=2*d,s3=3*d;
	for(int i=d;i>=1;i--){
		a[t++]=s1--;
		a[t++]=s2--;
		a[t++]=s3--;
	}
	
	if((n-m)%3==2){
		a[t++]=3*d+2;
		x=3*d+2;
	}
	
	x++;
	for(int i=t;i<=n+1;i++){
		a[i]=x++;
	}
	
	rep(i,1,n)cout<<a[i]<<' ';
	cout<<'\n';
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	int T = 1;
	cin >> T;
	while (T--) {
		solve();
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值