C题
题意:nim游戏,先手赢的话,尽量赢的快,输的话尽量输的慢。
求最多的游戏局数,和先手执行的最优策略数
下面给两个结论:
1,石子数异或和为0的话,先手败,否则胜
2,先手败的话,可以构造出先后手后面都只取一个石子。
结论1证明略。
下证结论2
先手必败,也就是说xor_sum=0
先考虑构造游戏局数尽量多的方案,先手在lowbit最小的石子堆里取一个。
后手只能对称地在另一个lowbit相等的堆里取一个。比如先手取的是…100,取完后变成了…011,xor_sum->xor_sum^(111),后手必须构造出(111)抵消掉影响。如果后手选lowbit大于先手lowbit的堆,那么会对更高位造成影响。所以后手只能选lowbit的石子堆,取一个石子。也就是说这样后手是和先手对称着操作的,对称着去lowbit相等的石子堆。
上面构造出一种方案,使得总局数为sum,再考虑上面情况下先手第一步取的方案数
再理一下条件,现在先手只能取一个,要使得后手也只能取一个。我们考虑什么情况下,后手可以多取。设先手取的石子堆lowbit是第i位,若存在另一堆第i位为1且i不为lowbit,则后手可以多取。比如先手取10100,存在00110,先手取后xor_sum->xor_sum^(111),后手可以把两个1都取掉抵消掉(111)的影响。
其他情况:
1,第i位为0,那么怎么操作到无法抵消掉先手造成的对第i位的影响。
2,第i位为1且为lowbit,那后手只能对称取一个石子。。。
证毕(乱证)
下面考虑先手胜,那第一步要尽可能取多,且使得剩下异或和为0。怎么求最大取掉多少呢。
设原异或和为S,要从ai中选t个石子取掉,剩下异或和0.
先扣除掉ai,则异或和为S^ai,只要ai>(S ^ ai)则存在,t = ai - (S ^ ai),使得S ^ (ai -t)=0.
维护t和次数即可。
很nb的思考题!
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define pii pair<int ,int >
#define pb(v) push_back(v)
#define all(v) v.begin(),v.end()
#define int long long
#define inf 0x3f3f3f3f
#define INF 0x7f7f7f7f7f7f7f7f
#define endl "\n"
#define fi first
#define se second
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
#define AC return 0
#define ldb long double
const int N=5e5+5;
const int mod=1e9+7;
const double eps=1e-8;
int n;
int a[N];
map<int,int> mp;
void solve(){
cin>>n;
int x_sum=0,sum=0;
_for(i,1,n){
cin>>a[i];
x_sum ^= a[i];
sum += a[i];
}
if( !x_sum ){
int cnt=0;
_for(i,1,n) mp[a[i]&-a[i]] = 0;
_for(i,1,n){
int t = a[i];
t -= t&-t;
while( t ){
mp[t&-t]=1;
t -= t&-t;
}
}
_for(i,1,n) if( !mp[a[i]&-a[i]] ) cnt++;
cout<<sum<<" "<<cnt<<endl;
}
else {
int mx = 0,cnt=0;
_for(i,1,n){
if( a[i] > (a[i]^x_sum) ){
int t = a[i] - (a[i]^x_sum);
if( t > mx ){
mx = t;
cnt=1;
}
else if( mx == t ) cnt++;
}
}
cout<<sum-mx+1<<" "<<cnt<<endl;
}
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
IOS;
int T;cin>>T;
while(T--) solve();
AC;
}
D题
题意:给n个物品间的兑换关系(n<=1e3),兑换关系如下a个x可以换wb个y,其中w是要求的,求最大的w,使得这些物品不能够无限制兑换。(无限制兑换指1个x换2个y,1个y能换10个x,这样可以无限兑换)
做法:考虑建图,二分w,然后check,边权为汇率,由于乘积很大,边权取个对,然后就是判有没有正环
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define pii pair<int ,int >
#define pb(v) push_back(v)
#define all(v) v.begin(),v.end()
#define int long long
#define inf 0x3f3f3f3f
#define INF 0x7f7f7f7f7f7f7f7f
#define endl "\n"
#define fi first
#define se second
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
#define AC return 0
#define double long double
const int N=1e3+5;
const int mod=1e9+7;
const double eps=1e-8;
int n,m;
int vis[N],cnt[N];
double dis[N];
std::vector< pair<int,double> > G[N];
queue<int> q;
bool ck(double mid,int st){//就是spaf求最短路
mst(dis,0);
mst(cnt,0);
queue<int> q;
_for(i,1,n){
q.push(i);
vis[i]=1;
}
while( !q.empty() ) {
int x = q.front();q.pop();
vis[x] = 0;
for(auto [y,len] :G[x]){
if( dis[x] + len + mid > dis[y] ){
dis[y] = dis[x] + len + mid;
cnt[y]++;
if( cnt[y] >= n ) return 0;
if( !vis[y] ){
vis[y] = 1;
q.push(y);
}
}
}
}
return 1;
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
// IOS;
cin>>n>>m;
_for(i,1,m){
int b,d;
double a,c;
cin>>a>>b>>c>>d;
G[b].push_back( {d,log2(c) - log2(a)});
}
double l = 0 , r = 1;
_for(i,1,50){
double mid = (l+r)/2;
if( ck(log2(mid),1) ) l = mid;
else r = mid;
}
printf("%.8Lf\n",r);
AC;
}
e题
题意:求长为n,含k个“bit”的字符串数,输出k从0到n的方案数,n<=1e6
做法:二项式反演+差卷积
考虑容斥,设
G
k
G_k
Gk表示恰好有k个bit的方案数,
F
k
F_k
Fk为钦定含k个bit的方案数
F
k
=
C
n
−
2
k
k
∗
2
6
n
−
3
k
F_k=C_{n-2k}^k*26^{n-3k}
Fk=Cn−2kk∗26n−3k
G
k
=
∑
j
=
k
n
(
−
1
)
j
−
k
∗
C
j
k
F
j
G_k=\sum_{j=k}^n(-1)^{j-k}*C_j^kF_j
Gk=∑j=kn(−1)j−k∗CjkFj,组合数展开后分离系数
k
!
∗
G
k
=
∑
j
=
k
n
(
j
!
F
j
)
∗
(
−
1
)
j
−
k
(
j
−
k
)
!
k!*G_k = \sum_{j=k}^n(j!F_j)*\frac{(-1)^{j-k}}{(j-k)!}
k!∗Gk=∑j=kn(j!Fj)∗(j−k)!(−1)j−k
发现右边是差卷积形式,翻转其中一个多项式后求卷积即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define pii pair<int ,int >
#define pb(v) push_back(v)
#define all(v) v.begin(),v.end()
#define int long long
#define inf 0x3f3f3f3f
#define INF 0x7f7f7f7f7f7f7f7f
#define endl "\n"
#define fi first
#define se second
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
#define AC return 0
#define ldb long double
const int maxn = 6e6+10;
const int p = 998244353;
int Pow(int x,int d){
int tans = 1;
if(d == 0)return 1%p;
int a = Pow(x,d/2);
tans = 1ll*a*a%p;
if(d%2)tans = 1ll*tans*x%p;
return tans%p;
}
typedef vector<int> Poly;//多项式定义
int F1[maxn],F2[maxn];
int rev[maxn];
void NTT(int * A,int lim,int opt) {
int i, j, k, m, gn, g, tmp;
for(int i = 0; i < lim; ++i)rev[i] = (i & 1)*(lim >> 1) + (rev[i >> 1] >> 1);
for(i = 0; i < lim; ++i)if (rev[i] < i) swap(A[i], A[rev[i]]);
for(m = 2; m <= lim; m <<= 1) {
k = m >> 1;
gn = Pow(3,(p - 1) / m);
for(i = 0; i < lim; i += m) {
g = 1;
for (j = 0; j < k; ++j, g = 1ll * g * gn % p) {
tmp = 1ll * A[i + j + k] * g % p;
A[i + j + k] = (A[i + j] - tmp + p) % p;
A[i + j] = (A[i + j] + tmp) % p;
}
}
}
if(opt == -1){
reverse(A+1,A+lim);
int inv = Pow(lim,p-2);
for(i = 0; i < lim; ++i) A[i] = 1ll * A[i] * inv % p;
}
}
Poly mul(const Poly & A,const Poly & B){
int n = A.size(),m = B.size();
int siz = n + m - 1;
Poly C(siz);
if(siz < 64){//小于等于64项直接暴力算
for(int i = 0;i < n;i++){
for(int j = 0;j < m;j++)C[i+j] = (C[i+j] + 1LL*A[i]*B[j]%p)%p;
}
return C;
}
int fsiz = 1;
while(fsiz <= siz)fsiz *= 2;
for(int i = 0;i < fsiz;i++)F1[i] = F2[i] = 0;
for(int i = 0;i < n;i++)F1[i] = A[i];
for(int i = 0;i < m;i++)F2[i] = B[i];
NTT(F1,fsiz,1);
NTT(F2,fsiz,1);
for(int i = 0;i < fsiz;i++)F1[i] = 1ll*F1[i]*F2[i]%p;
NTT(F1,fsiz,-1);
for(int i = 0;i < siz;i++){
C[i] = F1[i];
}
return C;
}
const int N = 1e6+10;
ll qsm(int a,int b){
int ans=1,tmp = a;
while( b ){
if( b&1 ) ans = (ans * tmp)%p;
tmp = (tmp * tmp)%p;
b>>=1;
}
return ans;
}
int fac[N],ni_f[N];
void ini(){
fac[0]=1;
int maxn = 1e6;
_for(i,1,maxn) fac[i] = (fac[i-1] * i)%p;
ni_f[maxn] = qsm(fac[maxn],p-2);
_rep(i,maxn-1,0) ni_f[i] = ni_f[i+1] * (i+1)%p;
}
ll C(int n,int m){
if( m==n || m==0 ) return 1;
if( n < m ) return 0;
return fac[n] * ni_f[n-m]%p * ni_f[m]%p;
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
IOS;
ini();
int n;
cin>>n;
Poly f(n+1,0);
_for(i,0,n){
if( n-3*i < 0 ) break;
f[i] = qsm(26,n-3*i)*C(n-2*i,i)%p*fac[i]%p;
}
Poly g(n+1,0);
_for(i,0,n){
int flag = ((n-i)&1)?-1:1;
g[i] = (p + flag*ni_f[n-i])%p;
}
Poly h = mul(f,g);
_for(i,n,2*n) cout<<h[i]*ni_f[i-n]%p<<" ";
AC;
}