第二十二届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;
}
本文来自博客园,作者:倔驴你真棒,转载请注明原文链接:https://www.cnblogs.com/Additionlly/p/18221879