[博弈搜索][概率][构造状态矩阵]
[COGS 2290 香蕉]
注意到6和15的转移情况以及值都是相同的,推论所有p1 ^ k1 * p2 ^ k2 ...
将指数sort一遍这些数字的转移情况都是相同的,10W以内的数字只有160种状态
构造一个转移矩阵然后做乘法即可。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define maxn 161
using namespace std;
const int md = 998244353;
typedef long long ll;
int Tot;
struct Matrix{
ll a[maxn][maxn];
void clear(){memset(a, 0, sizeof a);}
void set(){clear();for(int i = 1; i <= Tot; i ++)a[i][i] = 1;}
}mat, ans;
Matrix operator*(const Matrix& a, const Matrix &b){
Matrix c; c.clear();
for(int i = 1; i <= Tot; i ++)
for(int j = 1; j <= Tot; j ++){
int cnt = 0;
for(int k = 1; k <= Tot; k ++){
c.a[i][j] += a.a[i][k] * b.a[k][j];
cnt ++; if(cnt == 10)c.a[i][j] %= md, cnt = 0;
}
c.a[i][j] %= md;
}
return c;
}
int n, m;
#define M 100010
int p[M], Min[M], primes;
bool vis[M];
void preprime(){
Min[1] = 1;
for(int i = 2; i <= m; i ++){
if(!vis[i])p[primes ++] = i, Min[i] = i;
for(int j = 0; j < primes; j ++){
if(i * p[j] > m)break;
vis[i * p[j]] = true;
Min[i * p[j]] = p[j];
if(i % p[j] == 0)break;
}
}
}
ll hs[M], h[M];
int Log[10], c[10], cnt;
int Amt[maxn], _min[maxn];
Matrix power(Matrix a, int n){
Matrix ret; ret.set();
int cnt0 = 0, cnt1 = 0;
while(n){
if(n & 1)ret = ret * a;
n >>= 1;
a = a * a;
}
return ret;
}
int main(){
freopen("Banana.in", "r", stdin);
freopen("Banana.out", "w", stdout);
scanf("%d%d", &n, &m);
preprime();
for(int i = 1; i <= m; i ++){
int nw = i;
cnt = 0;
while(nw != 1){
int to = Min[nw], flag = false;
for(int j = 1; j <= cnt; j ++)
if(Log[j] == to){
c[j] ++;
flag = true;
break;
}
if(!flag)++ cnt, c[cnt] = 1, Log[cnt] = to;
nw /= to;
}
sort(c + 1, c + 1 + cnt);
ll ret = 0;
for(int j = 1; j <= cnt; j ++)
ret = ret * 100 + c[j];
h[i] = hs[i] = ret;
}
sort(h + 1, h + 1 + m);
Tot = unique(h + 1, h + 1 + m) - h - 1;
for(int i = 1; i <= m; i ++){
int pos = lower_bound(h + 1, h + 1 + Tot, hs[i]) - h;
Amt[pos] ++;
if(_min[pos] == 0)
_min[pos] = i;
}
int pos;
for(int i = 1; i <= Tot; i ++){
int nw = _min[i], q = sqrt(nw);
for(int j = 1; j <= Tot; j ++)
mat.a[j][i] += Amt[j];
for(int j = 1; j <= q; j ++){
if(nw == j || nw % j)continue;
pos = lower_bound(h + 1, h + 1 + Tot, hs[j]) - h;
mat.a[pos][i] --;
if(j * j == nw || j == 1)continue;
pos = lower_bound(h + 1, h + 1 + Tot, hs[nw / j]) - h;
mat.a[pos][i] --;
}
}
ans.clear();
for(int i = 1; i <= Tot; i ++)
ans.a[1][i] = 1;
ans = ans * power(mat, n);
cout << (ans.a[1][1] + md) % md<< endl;
return 0;
}
卡片(card)
【题目描述】鲲鹏国地震后,蛤布斯十分愤怒。他决定前往利沃夫动物园寻求帮助。利沃夫动物园的小象有n张卡片,第i张卡片上有一个数字ai。蛤布斯和小象轮流选择卡片 (不能重复),如果某次选择后已选择的卡片上的数字的gcd为1或者没有卡片可以选择,那么当前角色失败。蛤布斯想知道:1、如果双方都选择最优策略,谁会获胜;2、如果双方都随机选取,蛤布斯获胜的概率。有多组数据。
【输入数据】第一行一个整数t表示数据组数。 每组数据第一行一个整数n,第二行n个整数a1…an。
【输出数据】每组数据输出一行,包含一个整数和一个实数,用一个空格隔开。整数为1表示蛤布斯获胜,0表示小象获胜。实数表示获胜概率,保留4位小数。
【样例输入】
4
5
6 10 15 22 28
5
2 4 8 16 32
4
2 4 8 16
4
1 2 3 4
【样例输出】
0 0.4000
1 1.0000
0 0.0000
1 0.5833
【数据范围】
对于20%的数据,n<=20。
对于60%的数据,ai<=100。
对于100%的数据,t<=10,n<=100,ai<=10^5。
30分暴力。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#define maxn 100010
using namespace std;
#define M 2000000
int n, a[maxn], bo[M];
double f[M];
int gcd(int a, int b){
if(a == 0)return 0;
return !b ? a : gcd(b, a % b);
}
bool dfs(int S, int g){
if(S == 0)return false;
if(bo[S] != -1)return bo[S];
int cnt = 0, v = true;
for(int i = 0; i < n; i ++){
if(S >> i & 1){
cnt ++;
if(g && gcd(g, a[i]) == 1)continue;
if(a[i] == 1)continue;
if(g == 0) v &= dfs(S ^ (1 << i), a[i]);
else v &= dfs(S ^ (1 << i), gcd(g, a[i]));
f[S] += 1 - f[S ^ (1 << i)];
}
}
bo[S] = !v;
f[S] /= cnt;
return !v;
}
int main(){
freopen("card.in", "r", stdin);
freopen("card.out", "w", stdout);
int test;
scanf("%d", &test);
while(test --){
memset(bo, -1, sizeof bo);
memset(f, 0, sizeof f);
scanf("%d", &n);
for(int i = 0; i < n; i ++)
scanf("%d", &a[i]);
int S = 1 << n;
int flag = dfs(S - 1, 0);
printf("%d %.4lf\n", flag, (double)f[S - 1]);
}
return 0;
}
满分
card:
20%:状压dp。时间复杂度 O(tn2^n)。
60%:我们发现一个状态只和当前gcd和已选卡片数量有关,记录这两个信息即可。时间复杂度O(tmn^2),其中m为max(ai)。
100%:实际上状态数不到mn,而是sigma(ai的约数个数),这个值并不大。如果将ai中相同质因数去掉,状态数最多为n*2^6。时间复杂度O(tn^2*2^6)。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define maxn 100010
using namespace std;
typedef pair<double, int> type;
type f[maxn][110];
int vis[maxn][110], tim, a[maxn], n;
int gcd(int a, int b){return !b ? a : gcd(b, a % b);}
type dfs(int Gcd, int Num){
if(vis[Gcd][Num] == tim)return f[Gcd][Num];
type &Nw = f[Gcd][Num], ret; Nw.first = 0, Nw.second = 0;
vis[Gcd][Num] = tim;
if(Gcd == 1)return Nw = make_pair(1, 1);
if(Num == n)return Nw;
//-----------------------------------------------//
if(Gcd){
int cnt = 0; cnt -= Num;
for(int i = 1; i <= n; i ++)
cnt += (a[i] % Gcd == 0);
if(cnt > 0){
Nw = dfs(Gcd, Num + 1);
Nw.first = cnt * (1 - Nw.first), Nw.second ^= 1;
}
for(int i = 1; i <= n; i ++){
int to = gcd(a[i], Gcd);
if(to == 1 || to == Gcd)continue;
ret = dfs(to, Num + 1);
Nw.first += 1 - ret.first, Nw.second |= (ret.second ^ 1);
}Nw.first /= n - Num;
}
else{
for(int i = 1; i <= n; i ++){
ret = dfs(a[i], 1);
Nw.first += 1 - ret.first, Nw.second |= (ret.second ^ 1);
}Nw.first /= n;
}
return Nw;
}
int main(){
freopen("card.in", "r", stdin);
freopen("card.out", "w", stdout);
int test;
scanf("%d", &test);
while(test --){
tim ++;
scanf("%d", &n);
for(int i = 1; i <= n; i ++)
scanf("%d", &a[i]);
type ans = dfs(0, 0);
printf("%d %.4lf\n", ans.second, ans.first);
}
return 0;
}

浙公网安备 33010602011771号