Future Failure CodeForces - 838C (博弈论,子集卷积)

大意: 两人轮流操作一个长$n$, 只含前$k$种小写字母的串, 每次操作删除一个字符或者将整个串重排, 每次操作后得到的串不能和之前出现过的串相同, 求多少种串能使先手必胜.

找下规律发现$n$为奇数必胜, 否则假设$a_i$为字符$i$出现次数, 如果$\frac{n!}{a_1!a_2!...a_k!}$为奇数则必败

$n!$中$2$的幂次为n-__builtin_popcount(n)

所以必败就等价于$a_1+...+a_n=a_1|...|a_n$

设$f_{i,j}$表示前$i$个字符, 状态为$j$的方案数除以总字符数的阶乘

可以得到转移为$f_{i,S}=\sum \frac{1}{x!} f_{i-1,S\oplus x}$

做$O(\log k)$次子集卷积即可, 复杂度是$O(n\log ^2n\log k)$

我写的好像常数太大的没卡过去, 先这样吧

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl ‘\n‘
#define DB(a) ({REP(__i,1,n) cout<<a[__i]<<‘,‘;hr;})
using namespace std;
typedef long long ll;
const int N = 1e6+10;
int n,k,P,fac[N],ifac[N],cnt[N];
int dp[N],f[20][N],g[20][N],h[20][N];
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}

void FMT(int *a, int n, int tp) {
	int mx = (1<<n)-1;
	REP(i,0,n-1) REP(j,0,mx) {
		if (j>>i&1) a[j]=(a[j]+tp*a[j^1<<i])%P;
	}
}

void mul(int *a, int *b, int *c, int n) {
	int mx = (1<<n)-1;
	REP(i,0,n) REP(j,0,mx) f[i][j]=g[i][j]=h[i][j]=0;
	REP(i,0,mx) {
		f[cnt[i]][i] = a[i];
		g[cnt[i]][i] = b[i];
	}
	REP(i,0,n) FMT(f[i],n,1),FMT(g[i],n,1);
	REP(i,0,n) {
		REP(j,0,i) REP(k,0,mx) {
			h[i][k] = (h[i][k]+(ll)f[j][k]*g[i-j][k])%P;
		}
		FMT(h[i],n,-1);
		REP(k,0,mx) if (cnt[k]==i) c[k] = h[i][k];
	}
}

int main() {
	REP(i,0,N-1) cnt[i] = __builtin_popcount(i);
	scanf("%d%d%d",&n,&k,&P);
	fac[0] = 1;
	REP(i,1,N-1) fac[i]=(ll)fac[i-1]*i%P;
	ifac[N-1] = inv(fac[N-1]);
	PER(i,0,N-2) ifac[i]=(ll)ifac[i+1]*(i+1)%P;
	int tot = qpow(k,n);
	if (n&1) return printf("%d\n",tot),0;
	int len = 1;
	while ((1<<len)<=n) ++len;
	dp[0] = 1;
	for (; k; mul(ifac,ifac,ifac,len),k>>=1) {
		if (k&1) mul(dp,ifac,dp,len);
	}
	int ans = (tot-(ll)dp[n]*fac[n])%P;
	if (ans<0) ans += P;
	printf("%d\n", ans);
}

原文地址:https://www.cnblogs.com/uid001/p/11625021.html

时间: 2024-10-02 00:57:04

Future Failure CodeForces - 838C (博弈论,子集卷积)的相关文章

CF 914 G Sum the Fibonacci —— 子集卷积,FWT

题目:http://codeforces.com/contest/914/problem/G 其实就是把各种都用子集卷积和FWT卷起来算即可: 注意乘 Fibonacci 数组的位置: 子集卷积时不能一边做一边更新卷积的数组! 代码如下: #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; int rd() { int ret=

hdu 6057 Kanade&#39;s convolution(子集卷积)

题解: 然后就是接下来如何fwt 也就是如何处理bit(x) - bit(y) = bit(k)这个条件. 其实就是子集卷积. 把bit(x)和bit(y)划分成两个集合,然后就是子集卷积的形式. 这里设两个新的数组 A[bit(y)][y], B[bit(x)][x],代表拆出来的相应数组 然后对这两个数组做fwt,得到其点值表示,然后直接在外层枚举x和y的大小然后做卷积即可. 这样说可能很抽象,其实贴出代码就很清楚了 #include <iostream> #include <vec

CF914G Sum the Fibonacci FWT、子集卷积

传送门 一道良心的练习FWT和子集卷积的板子-- 具体来说就是先把所有满足\(s_a \& s_b = 0\)的\(s_a \mid s_b\)的值用子集卷积算出来,将所有\(s_a \oplus s_b\)用xor卷积算出来,把斐波那契数代进去,然后将三个数组and卷积,最后取\(2^i (i \in Z)\)的位置的答案的和 #include<bits/stdc++.h> //this code is written by Itst using namespace std; int

FMT 和 子集卷积

FMT 和 子集卷积 FMT 给定数列 $ a_{0\dots 2^{k}-1} $ 求 $ b $ 满足 $ b_{s} = \sum_{i\in s} a_i $ 实现方法很简单, for( i in 0..n-1 ) for( j in 0..2^n-1) if( j & ( 1 << i ) ) a[j] += a[j ^ ( 1 << i )] 然后称为 $ B = FMT(A) $ ,快速莫比乌斯变换 想要还原也很简单,把代码反着写: for( i in n-1

Financiers Game CodeForces - 737D (博弈论,区间dp)

大意: 给定$n$元素序列, 两个人从两端轮流拿数, 每一步假设对手上次取k, 那么只能取k或k+1, 先手第一步取1或2, 直到不能拿时停止. 先手要最大化两人数字和的差, 后手要最小化, 求最后差是多少. 显然状态数是$O(n^2)$的, 直接暴力DP #include <iostream> #include <sstream> #include <algorithm> #include <cstdio> #include <math.h>

Tokitsukaze and Duel CodeForces - 1191E (博弈论)

大意: 给定01串, 两人轮流操作, Tokitsukaze先手. 每次操作可以选择长为$k$的区间, 全部替换为$0$或$1$, 若替换后同色则赢. 求最后结果. 先判断第一步是否能直接赢, 不能的话若所有后继都是必败则必败, 否则平局. 正确性很显然, 因为一次操作不能直接赢的话, 只要模仿对手操作一定能平局. 那么问题就转化为判断一步操作后是否能赢. 假设$0$的最大范围为$[L[0],R[0]]$,$1$的最大范围为$[L[1],R[1]]$, 那么只要操作前$R[0]-L[0]+1\l

集合并卷积的三种求法

也许更好的阅读体验 本文主要内容是对武汉市第二中学吕凯风同学的论文<集合幂级数的性质与应用及其快速算法>的理解 定义 集合幂级数 为了更方便的研究集合的卷积,引入集合幂级数的概念 集合幂级数也是形式幂级数的一种,只是集合的一种表现形式,无需考虑收敛或发散的含义 定义一个集合 \(S\) 的集合幂级数为 \(f\) ,那么我们就可以把集合 \(S\) 表示为如下形式 \(\begin{aligned}f=\sum _{T\subseteq S}f_{T}\cdot x^{T}\end{align

July 20th, Week 30th Wednesday, 2016

Learn from yesterday, live for today, and hope for tomorrow. 借鉴昨天,活着当下,憧憬未来. Yesterday is the past, we can learn something from it, such as why we succeeded, why we failed, what we can do to improve ourselves. Bear in your mind that it is no use to c

Mesos源码分析(12): Mesos-Slave接收到RunTask消息

在前文Mesos源码分析(8): Mesos-Slave的初始化中,Mesos-Slave接收到RunTaskMessage消息,会调用Slave::runTask. ? void Slave::runTask( ????const UPID& from, ????const FrameworkInfo& frameworkInfo, ????const FrameworkID& frameworkId_, ????const UPID& pid, ????TaskInfo