前言
别样的数数大战。
别样的容斥大战。
别样的背包大战。
结果是 100+100+0+0。
Rating 变化:\(2120 \to 2121\)。何意味。
推歌:A4。 - 天使の翼。。
T1 好数
题意
给定 \(n\) 长的序列 \(a\)。
称一个数 \(a_i\) 是好的,当且仅当 \(\exists 1\le j,k,l \lt i,a_j+a_k+a_l=a_i\)。
求好数的数量。
题解
注意到 \(n\le 5000\),\(O(n^2)\) 可做。
所以考虑维护 \([1,i-1]\) 内所有二元组之和,开个桶记录有没有出现过。然后从 \(i\) 向前扫,带上二元组就刚好满足要求。
当然,\(O(\frac{n^3}{w})\) 也能过,而且我一开始想的确实是这个。但我懒得写了。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3f
using namespace std;int n;
long long a[5010];
bool v[400010];int main(){ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>n;for(int i=1;i<=n;i++) cin>>a[i];int ans=0;for(int i=1;i<=n;i++){bool flg=false;for(int j=1;j<i&&!flg;j++) flg|=v[a[i]-a[j]+200001];for(int j=1;j<=i;j++) v[a[i]+a[j]+200001]=true;ans+=flg;}cout<<ans<<"\n";# ifndef ONLINE_JUDGEcerr<<"\nUsed time: "<<clock()*1.0/CLOCKS_PER_SEC<<"s.\n";# endifreturn 0;
}
T2 SOS 字符串
题意
数数。
求有多少个长度为 \(n\) 的字符串包含至少三个 SOS 子串。同一个字符只能被包含一次,如我们认为 SOSOS 只包含一个 SOS。
题解
设 \(dp_{i,0/1/2,0/1/2}\) 表示当前在第 \(i\) 个字符,已经匹配了 \(0/1/2\) 个 SOS,当前匹配进度为 \(0/1/2\) 时的字符串总数。
匹配进度定义如下:
- \(0\):前一个字符是
S以外的字符或前两个字符是SO以外的字符。 - \(1\):前一个字符是
S。 - \(2\):前两个字符是
SO。
注意两个 S 不能共用,所以每次从状态 \(2\) 匹配完成 SOS 后应该返回到状态 \(0\)。
如果 SOS 已经匹配了三次,那么可以直接把当前种类数累加到答案中。同时后面的 \(n-i\) 个字符可以任意取,记得乘上系数。
时间复杂度 \(O(n\log n)\)。可以通过每次把 \(ans\gets ans\times 26\) 把 $\log $ 去掉。
或者注意到当前状态只和前一个状态有关,所以可以用矩乘优化到 \(O(P^3\log n)\),其中 \(P=10\),代表状态数。但我懒得写了。
不卡空间,所以也懒得滚了。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3f
using namespace std;const long long mod=1e9+7;
long long qpow(long long x,long long a){long long res=1;while(a){if(a&1) res=res*x%mod;x=x*x%mod;a>>=1;}return res;
}int n;
long long dp[1000010][3][3]; // pre i, com j, nowchar k.
// k=0: empty, k=1: S, k=2: SO.int main(){ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>n;dp[0][0][0]=1;long long ans=0;for(int i=0;i<n;i++){// 失配情况。注意 S 后加 O 不会失配。(dp[i+1][0][0]+=dp[i][0][0]*25)%=mod;(dp[i+1][0][0]+=dp[i][0][1]*24)%=mod;(dp[i+1][0][0]+=dp[i][0][2]*25)%=mod;(dp[i+1][1][0]+=dp[i][1][0]*25)%=mod;(dp[i+1][1][0]+=dp[i][1][1]*24)%=mod;(dp[i+1][1][0]+=dp[i][1][2]*25)%=mod;(dp[i+1][2][0]+=dp[i][2][0]*25)%=mod;(dp[i+1][2][0]+=dp[i][2][1]*24)%=mod;(dp[i+1][2][0]+=dp[i][2][2]*25)%=mod;// S 后加 S。(dp[i+1][0][1]+=dp[i][0][1])%=mod;(dp[i+1][1][1]+=dp[i][1][1])%=mod;(dp[i+1][2][1]+=dp[i][2][1])%=mod;// 匹配成功。empty->S; S->SO; SO->SOS;(dp[i+1][0][1]+=dp[i][0][0])%=mod;(dp[i+1][0][2]+=dp[i][0][1])%=mod;(dp[i+1][1][0]+=dp[i][0][2])%=mod;(dp[i+1][1][1]+=dp[i][1][0])%=mod;(dp[i+1][1][2]+=dp[i][1][1])%=mod;(dp[i+1][2][0]+=dp[i][1][2])%=mod;(dp[i+1][2][1]+=dp[i][2][0])%=mod;(dp[i+1][2][2]+=dp[i][2][1])%=mod;(ans+=dp[i][2][2]*qpow(26,n-(i+1))%mod)%=mod; // 直接累加进答案。}cout<<ans<<"\n";# ifndef ONLINE_JUDGEcerr<<"\nUsed time: "<<clock()*1.0/CLOCKS_PER_SEC<<"s.\n";# endifreturn 0;
}
T3 集训营的气球
题意
有 \(n\) 位顾客,每位顾客都会选择购买物品 A 或物品 B。如果选择买物品 A,则至多买 \(a_i\) 个;如果选择购买物品 B,则至多买 \(b_i\) 个。必须选择一种物品购买,且至少购买一个。
除此之外存在限制 \(c\):顾客中至少需要有 \(c\) 个人购买物品 A。
现在顾客会更改他们的需求 \(q\) 次,询问每次更改需求后所有顾客一共有多少种购买的方案。
题解
先思考 \(c=0\) 怎么做。不难发现答案形如 \(\prod _{i=1}^{n}a_i+b_i\)。
然后发现 \(1\le c\le 20\),启发我们计算不合法的情况数量,即使用 hxf 容斥。
不难发现这是一坨 01 背包状物。设 \(f_{i}\) 表示有且仅有 \(i\) 个人购买了物品 A 的方案总数。
然后发现这玩意一无序二可撤销,所以可以通过退背包维护。
每次询问之前把这个人的贡献撤销掉,然后再重新计算新贡献,再用 \(c=0\) 的总数减去不合法的就是答案。
设 \(f\) 为考虑第 \(x\) 个人之前的情况,\(g\) 为考虑第 \(x\) 个人之后的情况。这两个式子可以互相转化。
因为 01 背包的计算顺序是从大到小,所以退背包的时候需要从小到大退掉。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3f
using namespace std;const long long mod=1e9+7;
long long qpow(long long x,long long a){long long res=1;while(a){if(a&1) res=res*x%mod;x=x*x%mod;a>>=1;}return res;
}int n,c,q;
long long a[1000010],b[1000010];long long nowtot=1;
long long f[21];int main(){ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>n>>c;for(int i=1;i<=n;i++) cin>>a[i];for(int i=1;i<=n;i++) cin>>b[i];f[0]=1;for(int i=1;i<=n;i++) (nowtot*=(a[i]+b[i]))%=mod;for(int i=1;i<=n;i++){for(int j=c-1;j>0;j--) f[j]=(f[j-1]*a[i]%mod+f[j]*b[i]%mod)%mod;(f[0]*=b[i])%=mod;}cin>>q;while(q--){int p;long long x,y;cin>>p>>x>>y;long long inv_b=qpow(b[p],mod-2);nowtot=(nowtot*qpow(a[p]+b[p],mod-2)%mod*(x+y)%mod);(f[0]*=inv_b)%=mod;for(int i=1;i<c;i++) f[i]=(f[i]+(mod-f[i-1]*a[p]%mod)%mod)*inv_b%mod;a[p]=x;b[p]=y;for(int i=c-1;i>0;i--) f[i]=(f[i-1]*a[p]%mod+f[i]*b[p]%mod)%mod;(f[0]*=b[p])%=mod;long long ans=nowtot;for(int i=0;i<c;i++) (ans+=(mod-f[i])%mod)%=mod;cout<<ans<<"\n";}# ifndef ONLINE_JUDGEcerr<<"\nUsed time: "<<clock()*1.0/CLOCKS_PER_SEC<<"s.\n";# endifreturn 0;
}
总结
数数专场。
hxf 容斥专场。
T2 被啥比容斥做法硬控了 $\inf $ 分钟。然后才反应过来能 DP。