第二十二届SCU程序设计竞赛(Tencent CUP)

Problem A. 配对质数

此题事先把素数筛出来,由于从前往后可能会导致后面的数字无法配对,我们只需从后往前,把数x/2得到的两个数,即可完成配对操作

#include <iostream>
#include <vector>
#include <algorithm>
 
using namespace std;

template <typename T>
inline void read(T &x)
{
    T f = 1;
    x = 0;
    char ch = getchar();
    while (0 == isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (0 != isdigit(ch))
        x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
    x *= f;
}
 
template <typename T>
inline void write(T x)
{
    if (x < 0)
    {
        x = ~(x - 1);
        putchar('-');
    }
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}

int n;
int primes[4100100],cnt,vis[2000100],a[2000100],b[2000100],q;
bool st[4100100];
void init()
{
	st[1]=true;
	for(int i=2;i<=4100000;i++)
	{
		if(!st[i]) primes[++cnt]=i;
		for(int j=1;primes[j]*i<=4100000;j++) 
		{
			st[primes[j]*i]=true;
			if(i%primes[j]==0) break;
		}
	}
}
void solve()
{
	int flag=1;
//	vpii vec;
	scanf("%d",&n);
	for(int i=1;i<=2*n;i++)
	{
		vis[i]=0;
	}
	int p=lower_bound(primes+1,primes+cnt+1,4*n-1)-primes;
	q=1;
	for(int i=2*n;i>=1;i--)
	{
		if(vis[i]) continue;
		int j=primes[p]-i;
		while(j<1||j>=i||vis[j])
		{
			p--;
			if(p<1) break;
			j=primes[p]-i;
		}
		if(p<1)
		{
			flag=0;
			break;
		}
		a[q]=i;
		b[q++]=j;
		vis[i]=vis[j]=1;
	}
	if(!flag) printf("-1\n");
	else
	{
		for(int i=1;i<=n;i++)
		{
			printf("%d %d\n",a[i],b[i]);
		}
	}
}
int main()
{
	init();
	int _=1;
	scanf("%d",&_);
	while(_--)
	{
		solve();
	}
	return 0;
}

Problem B 问路

题目限制 X < 1.5R ,我们可以得到 n < 90度的,求出圆心角之后它的cos就是r的半径,再求出在r上走X长度的圆心角度数(有可能走好几圈,需要判断一下),在R上找到两点最短距离即可(可能是顺时针,也能能是逆时针)。

#include<iostream>
#include<cmath>
using namespace std;

int main(){
    std::ios::sync_with_stdio(false);
    double R, x;
    scanf("%lf %lf", &R, &x);
    if(R == 0 || x == 0){
        printf("0");
        return 0;
    }
    // cin >> R >> x;
    double tmp = cos(x / R);
    double r = tmp * R;
    double a = x * 180 / (r * 3.1415926);
    a = a - int(a / 360) * 360;
    double l = a / 180 * 3.1415926 * R;
    double c = 2 * 3.1415926 * R;
    double res = min(l - x, c - l + x);
    printf("%lf", res);
    // cout << x * R / r - x << endl;
    return 0;
}

Problem C 大魔法师

此题为01背包问题,首先处理3种种类在每个体积下的最大值,在遍历3种每个体积的乘积求出最大值o(n^2),输出即可

#include<iostream>

using namespace std;

int v[2200][2200], w[2200][2200];
int f[4][2200];
int x[2200];

int main(){
    std::ios::sync_with_stdio(false);
    int n;
    cin >> n;
    for(int i = 1; i <= 3; i ++) cin >> x[i];
    for(int i = 1; i <= 3; i ++)
        for(int j = 1; j <= x[i]; j ++)
            cin >> v[i][j] >> w[i][j];

    for(int i = 1; i <= 3; i ++)
        for (int j = 1; j <= x[i]; j ++ )
            for (int k = n; k >= v[i][j]; k -- )
                f[i][k] = max(f[i][k], f[i][k - v[i][j]] + w[i][j]);
    long long res = 0;
    for(int i = 0; i <= n; i ++){
        for(int j = 0; j <= n; j ++){
            int t = n - i - j;
            if(t > 0){
                res = max(res, (long long)f[1][i] * f[2][j] * f[3][t]);
            }
            else
                break;
        }
    }

    cout << res;
    return 0;
}
/*
    10
    2 2 2
    2 1
    3 2
    2 1
    3 2
    5 3
    4 2
*/

Problem F 溶液配制I

找到大于X的溶液数量和小于X的溶液数量,用总数减去不能构成的数量就是能构成的数量,预处理所有的可能,每个溶液只有两种可能2^n - 1就为某一种情况的所有可能

#include<iostream>
#include<algorithm>
using namespace std;
const int mod=1e9+7,N=1e5+10;
typedef long long LL;
double f[100100];
long long s[100100];
int main()
{
    int n, q;
    scanf("%d %d", &n, &q);
    for(int i = 0; i < n; i ++)
        cin >> f[i];
    sort(f, f + n);
    long long tt = 1;
    for(int i = 1; i < 1e5 + 7; i ++){
        s[i] = (s[i - 1] + tt) % mod;
        tt = tt * 2 % mod;
    }
    for(int kk = 0; kk < q; kk ++)
    {
        double x;
        scanf("%lf", &x);
        int k1 = lower_bound(f, f + n, x) - f;
        int a = 0, b = 0;
        a = k1;
        int cnt = 0;
        while(f[k1] == x &&  k1 < n ){
            cnt ++;
            k1 ++;
        }
        b = n - a - cnt;
        long long sum = 0;
        // cout << a << " " << b << endl;
        int r = min(a, b);
        int l = max(a, b);
        
        if(r == 0) {
            sum = (s[n] - s[l] + mod) % mod;
            // for(int i = l + 1; i <= n; i ++)
            //     sum = (sum + aa[i + 1]) % mod;
        }

        else if(l == 0)
            sum = 0;
        else{
            sum = 1;
            sum = (sum + s[l] - s[r] + mod) % mod;
            sum = (sum + s[n] - s[l + 1] + mod) % mod;
            // for(int i = r + 1; i <= l; i ++)
            //     sum = (sum + aa[i]) % mod;
            // for(int i = l + 2; i <= n; i ++)
            //     sum = (sum + aa[i]) % mod;
        }
        printf("%lld\n", sum);
    }
    return 0;
}

Problem L 道路

构建一颗树,找的每个叶结点的深度,根据题意从小到大一次遍历叶结点,得到的极差就是res,斜树的极差是0...

#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;

struct node{
    int p = 0, s = 0, d = 0, idx = 0;
}nd[200020];
int fp[200020];
vector<int> q[200020];

bool st[200010];

bool cmp(node a, node b){
    if(a.s < b.s)
        return true;
    if(a.s == b.s)
        return a.d < b.d;
    return false;
}

void dfs(int u, int x){
    nd[u].d = x;
    if(!nd[u].s){
        return ;
    }
    int len = nd[u].s;
    for(int i = 0; i < len; i ++)
        dfs(q[u][i], x + 1);
    return ;
}

int cal(int x){
    int res = 0;
    while(fp[x] != 0){
        if(!st[x]){
            res ++;
            st[x] = true;
        }
        x = fp[x];
    }
    return res;
}

int main(){
    std::ios::sync_with_stdio(false);
    int n;
    cin >> n;
    nd[1].d = nd[1].p = 0;
    nd[1].idx = 1, nd[1].s = 0;
    for(int i = 2; i <= n; i ++) {
        cin >> nd[i].p;
        fp[i] = nd[i].p;
        q[nd[i].p].push_back(i);
        nd[nd[i].p].s ++;
        nd[i].idx = i;
    }
    dfs(1, 0);
    sort(nd + 1, nd + n + 1, cmp);
    int Max = 0, Min = 200010;
    int cnt = 0;
    for(int i = 1; !nd[i].s; i ++){
        cnt ++;
        int t = cal(nd[i].idx);
        Max = max(Max, t);
        Min = min(Min, t);
    }
    cout << Max - Min;
    return 0;
}

Problem M 石子游戏

博弈论,我刚开始没理解题意,我们可以发现,每隔3个数为一个循环,我们可以看出,如果选前一个0的位置就往后移一位,由此我们可以得到一般条件下,当m是2的倍数时,Alice获胜,边界条件时 1,3 Alice胜

#include<iostream>

using namespace std;

int main(){
    std::ios::sync_with_stdio(false);
    int n;
    cin >> n;
    for(int i = 0; i < n; i ++){
        int m;
        cin >> m;
        if(m == 1 || m == 3 || (m % 2 == 0 && m != 2))
            cout << "Alice" << endl;
        else
            cout << "Bob" << endl;
    }
    return 0;
}

 

posted @ 2024-05-30 10:28  倔驴你真棒  阅读(208)  评论(0)    收藏  举报