题目链接
1006 GCD Game
思路
就是要将这道题转化成Nim博弈的形式,因为每一次能从一个数转到任何一个质因数的位置,因此就相当于有n堆石子,每一堆石子的个数就是这个数的质因数的个数。因此先打表,将107之内的数的质因数的个数先表示出来,然后用到的时候直接查询即可。
这里将Nim博弈说一下:
给定n堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。问如果两人都采用最优策略,先手是否必胜。
结论:
必胜状态(a1^ a2 …^an!=0):可以走到某一个必败状态。
必败状态(a1^ a2 …^an==0):走不到任何一个必败状态。
代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN=1e7+10;
int prime[MAXN/2];//存储素数
bool noprime[MAXN];//用于标记是否是素数
int num[10000010];
void init()
{
memset(noprime,false,sizeof(noprime));
int sk=0;
for (int i=2; i<=MAXN; i++)
{
if (!noprime[i])
{
prime[++sk]=i;
num[i]=1;
}
for (int j=1; j<=sk&&i*prime[j]<=10000000; j++)
{
noprime[i*prime[j]]=1;
num[i*prime[j]]=num[i]+1;
if (i%prime[j]==0) break;
}
}
}
int main()
{
init();
int t,n;
scanf("%d",&t);
while(t--)
{
int ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
ans^=num[x];
}
if(ans)
printf("Alice\n");
else
printf("Bob\n");
}
return 0;
}
1008 Square Card
思路
其实这个思路很简单,就是两个圆之间相交的面积和大圆的面积的比值。只不过这个题我们不能直接比,还要考虑一种情况就是,这个正方形能放到圆里面还需要满足的条件就是这个方形还要完全能够放在这个圆里面。极端情况就是正方形的两个点与圆相交,如下图所示:
图中黑线画出的一段距离就是我们真正要求的圆的半径,其长度可以得出为:
sqrt(r2-(a/2)2)-(a/2)。
代码
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define pi 3.141592653589793238462643383279
#define eps 1e-6
using namespace std;
typedef long long ll;
typedef struct node
{
int x;
int y;
}point;
double calc(point a, double r1, point b, double r2)//计算两圆的相交面积
{
double d = sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
if (d >= r1+r2)
return 0;
if (r1>r2)
{
double tmp = r1;
r1 = r2;
r2 = tmp;
}
if(r2 - r1 >= d)
return pi*r1*r1;
double ang1=acos((r1*r1+d*d-r2*r2)/(2*r1*d));
double ang2=acos((r2*r2+d*d-r1*r1)/(2*r2*d));
return ang1*r1*r1 + ang2*r2*r2 - r1*d*sin(ang1);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
double r1, x1, y1, r2, x2, y2, aa;
scanf("%lf%lf%lf%lf%lf%lf%lf", &r1, &x1, &y1, &r2, &x2, &y2, &aa);
if (r1 * r1 - aa * aa / 4 <= 0 || r2 * r2 - aa * aa / 4 <= 0)
{
cout << "0.000000" << endl;
continue;
}
point a,b;
a.x=x1;
a.y=y1;
b.x=x2;
b.y=y2;
double r3 = sqrt(r1 * r1 - aa * aa / 4) - aa / 2, r4 = sqrt(r2 * r2 - aa * aa / 4) - aa / 2;
double res = calc(a, r3, b, r4);
printf("%.6lf\n", res / (pi * r3 * r3));
}
return 0;
}
1009 Singing Superstar
思路
这道题是队友做出来的,待写。
代码
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define pi 3.141592653589793238462643383279
#define eps 1e-6
using namespace std;
typedef long long ll;
const int N = 1e5 + 5, M = 6 * 1e5 + 5;
int n, ty[N], T, len, tr[M][26], cnt[N][2], ne[M], fail[M], last[N], vis[N]; //last保存上一次匹配的位置
char s[N], sp[N][100];
void add(int id)
{
int p = 0;
for (int i = 0; sp[id][i]; i++)
{
int j = sp[id][i] - 'a';
if (!tr[p][j]) tr[p][j] = ++len;
p = tr[p][j];
}
if (!fail[p]) fail[p] = id;
else
{
vis[id] = fail[p]; //代表重复的串 那么直接用前面的id就行了。
}
}
void build()
{
queue<int> q;
for (int j = 0; j < 26; j++)
{
if (tr[0][j]) q.push(tr[0][j]);
}
while (!q.empty())
{
int p = q.front();
q.pop();
for (int j = 0; j < 26; j++)
{
int c = tr[p][j];
if (!c) tr[p][j] = tr[ne[p]][j];
else
{
ne[c] = tr[ne[p]][j];
q.push(c);
}
}
}
}
int main()
{
int T;
scanf("%d\n",&T);
while(T--)
{
scanf("%s%d", s, &n);
memset(tr, 0, sizeof(tr));
memset(ne, 0, sizeof(ne));
memset(vis, 0, sizeof(vis));
memset(fail, 0, sizeof(fail));
memset(last, -1, sizeof(last));
len = 0;
memset(cnt, 0, sizeof(cnt));
for (int i = 1; i <= n; i++)
{
scanf("%s", sp[i]);
ty[i]=1;
add(i);
}
build();
for (int i = 0, j = 0; s[i]; i++)
{
int k = s[i] - 'a';
j = tr[j][k];
int t = j;
while (t)
{
if (fail[t])
{
cnt[fail[t]][0]++;
int cha = i - last[fail[t]] - strlen(sp[fail[t]]);
if (cha >= 0) cnt[fail[t]][1]++, last[fail[t]] = i;;
}
t = ne[t];
}
}
for (int i = 1; i <= n; i++)
{
if (!vis[i])printf("%d\n", cnt[i][ty[i]]);
else printf("%d\n", cnt[vis[i]][ty[i]]);
}
}
return 0;
}