题解 团不过
又一个神仙题
- 貌似有个思路是处理形如「异或和为零」的限制时,让前 \(n-1\) 位随便选,通过调整最后一位将异或和消成0
令 \(p(i)\) 为 \(i\) 堆的所有选法数,\(p(i)=(2^n-1)^{\underline{i}}\)
令 \(f(i)\) 为 \(i\) 堆所有异或和为零的方案数
计算 \(f(i)\) 的时候考虑用所有方案数减去不合法方案数
前 \(i-1\) 位确定了,最后一位就确定了,所以加上 \(p(i-1)\),这里一开始想假了
若前 \(i-1\) 位异或和为0,则第 \(i\) 位没有合法方案,所以减去 \(f(i-1)\)
然后考虑去掉重复使用同一个元素的
若有一个元素与第 \(i\) 个相同,即在前 \(i-1\) 堆中有 \(i-2\) 堆异或和为0
于是枚举另一堆在哪及值为多少,减去 \((i-1)(2^n-1-(i-1))f(i-2)\)
于是可以 \(O(n)\) 递推
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 10000010
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
const ll mod=1e9+7;
inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a%mod,b>>=1) if (b&1) ans=ans*a%mod; return ans;}
namespace force{
int lim;
ll fac[N], ans;
void dfs(int u, int lst, int sum) {
if (u>n) {
if (sum) ++ans;
return ;
}
for (int i=lst+1; i<lim; ++i)
dfs(u+1, i, sum^i);
}
void solve() {
lim=1<<n;
fac[0]=fac[1]=1;
for (int i=2; i<=n; ++i) fac[i]=fac[i-1]*i%mod;
dfs(1, 0, 0);
printf("%lld\n", ans*fac[n]%mod);
exit(0);
}
}
namespace task{
ll p[N], f[N];
void solve() {
p[0]=1;
ll tem=qpow(2, n);
for (int i=1; i<=n; ++i) p[i]=p[i-1]*(tem-i)%mod;
for (int i=3; i<=n; ++i) f[i]=(p[i-1]-f[i-1]-(i-1)*(tem-i+1)%mod*f[i-2]%mod)%mod;
printf("%lld\n", ((p[n]-f[n])%mod+mod)%mod);
exit(0);
}
}
signed main()
{
freopen("yui.in", "r", stdin);
freopen("yui.out", "w", stdout);
n=read();
task::solve();
return 0;
}