牛客练习赛65 A 最值序列 B 多重序列 C 二维动点

给一个长度为n的序列a 一开始你有一个数A = 0,每次可以从序列中选一个数b,令A = A + b或者A = A * b,每个数都要使用一次,加的次数要和乘的次数相同,要求最大化A,输出A对998244353取模的值

n≤5×105且为偶数,1≤ai≤109

排序一下,前一半用加,后一半用乘。

注意取mod

#include<algorithm>
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long LL;
const int N = 5e5+10 , mod = 998244353;
inline int read()
{
	register int x = 0; register char c = getchar();
	while(c < '0' || c > '9') c = getchar();
	while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48) , c = getchar();
	return x;
}
int n;
int a[N];
inline int add(int a , int b) { a += b; return a >= mod ? a - mod : a; }
inline int mul(int a , int b) { return (LL)a * b % mod; }
int main()
{
	n = read();
	for(int i = 1 ; i <= n ; ++i) a[i] = read();
	sort(a + 1 , a + 1 + n);
	for(int i = 1 ; i <= n ; ++i) a[i] %= mod;
	int res = 0;
	for(int i = 1 ; i <= n / 2 ; ++i) res = add(res , a[i]);
	for(int i = n / 2 + 1 ; i <= n ; ++i) res = mul(res , a[i]);
	cout << res << '
';
	return 0;
}

B 多重序列

给出n个组,第i组有m个数,分别为(a_{i,j}),一组数的权值表示为该组数所有数的乘积,找出权值最大的组,输出权值对mod取模后的值

对于每组数据给出一个k,保证(a_{i,j})是k的非负整数次幂

1≤n,m≤2000,1≤k≤100,1≤ai,j,mod≤1012

注意一下k=1的情况基本就能过 , 然而我的常数比较大,加了一点点小优化才过。

#include<algorithm>
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long LL;
#define int long long
const int N = 2e3+10;
inline LL read()
{
	register LL x = 0; register char c = getchar();
	while(c < '0' || c > '9') c = getchar();
	while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48) , c = getchar();
	return x;
}
int n , m , k;
LL mod;
int cnt[N] , wk[N];

LL mul(LL a , LL k)
{
	a %= mod; LL ans = 0;
	for( ; k ; k >>= 1 , a = (a + a) % mod)
		if(k & 1) ans = (ans + a) % mod;
	return ans;
}

LL ksm(LL a , LL k)
{
	a %= mod; LL ans = 1;
	for( ; k ; k >>= 1 , a = a * a % mod) 
		if(k & 1) ans = ans * a % mod;
	return ans;
}

signed main()
{
	n = read(); m = read(); k = read(); mod = read();
	if(k == 1LL) return puts("1") , 0;
	wk[0] = 1; int tot = 1;
	for( ; 1 ; ++tot)
	{
		wk[tot] = wk[tot-1] * k;
		if(wk[tot] > 1e12) break;
	}
	for(int i = 1 ; i <= n ; ++i)
	{
		LL x;
		for(int j = 1 ; j <= m ; ++j)
		{
			x = read(); 
			int p = lower_bound(wk , wk + 1 + tot , x) - wk;
			cnt[i] += p;
		}
	}
	int mx = 0;
	for(int i = 1 ; i <= n ; ++i) mx = max(mx , cnt[i]);
	cout << ksm(k , mx) << '
';
	return 0;
}

C 二维动点

一个二维平面上有n个点((a_i,b_i)),在一次移动中,你可以选择一个不和当前所在位置重叠的点,然后可以移动到当前所在位置和选择的点构成的直线上的任何一个位置;每次询问一个点(x,y),求出从(0,0)到达(x,y)所需要的最少移动次数,无解输出-1

大胆猜结论, (n > 2 , 最大是2)

大力无脑特判,

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<set>
using namespace std;
typedef long long LL;
const int N = 1e5+10;
inline int read()
{
    register int x = 0; register char c = getchar();
    while(c < '0' || c > '9') c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48) , c = getchar();
    return x;
}
set< pair<int,int> > s;
int a[N] , b[N];
 
int Gcd(int a , int b) { return !b ? a : Gcd(b , a % b); }
 
int main()
{
    int n = read() , Q = read() , x , y , gcd;
    for(int i = 1 ; i <= n ; ++i)
    {
        a[i] = read(); b[i] = read(); gcd = Gcd(a[i] , b[i]);
        if(a[i] == 0 && b[i] == 0) {i --; n --; continue; }
        s.insert(make_pair(a[i] / gcd , b[i] / gcd));
    }
    while(Q--)
    {
        x = read(); y = read(); gcd = Gcd(x , y);
        if(x == 0 && y == 0) puts("0");
        else
        if(s.count(make_pair(x / gcd , y / gcd))) puts("1");
        else
        if(n < 2 || (int)s.size() == 1) puts("-1");
        else
        if(n > 2) puts("2");
        else
        if(a[1] + a[2] == x && b[1] + b[2] == y) puts("3");
        else puts("2");
    }
    return 0;
}