B. Light bulbs
题意:
起初\(n\)个位置状态为\(0\),\(m\)次操作,每次操作更换区间状态:\(0\)到\(1\),\(1\)到\(0\)。
共有\(T,T\leq 1000\)组数据,\(n\leq 10^6,m\leq 1000\)。
最后输出状态为\(1\)的个数和。
思路:
一开始冲了一发维护差分,最后求出前缀和,成功\(TLE\)。
其实只要发现有用的点只有\(2m\)个,之后从左到右扫一遍,类似于扫描线的思想,累加贡献就行了。
Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e3 + 5;
int T;
int n, m;
struct Istream {
template <class T>
Istream &operator >>(T &x) {
static char ch;static bool neg;
for(ch=neg=0;ch<'0' || '9'<ch;neg|=ch=='-',ch=getchar());
for(x=0;'0'<=ch && ch<='9';(x*=10)+=ch-'0',ch=getchar());
x=neg?-x:x;
return *this;
}
}fin;
pii x[N << 1];
int main() {
fin >> T;
int tot = 0;
while(T--) {
fin >> n >> m;
for(int i = 1; i <= m; i++) {
int l, r; fin >> l >> r;
x[2 * i - 1] = MP(l, 0), x[2 * i] = MP(r + 1, 1);
}
sort(x + 1, x + 2 * m + 1, [&](pii A, pii B) {
if(A.fi == B.fi) return A.se < B.se;
return A.fi < B.fi;
});
int cnt = 0, ans = 0, last = 0;
for(int i = 1; i <= 2 * m; i++) {
if(x[i].se == 0) {
if(cnt & 1) {
ans += x[i].fi - last;
}
++cnt;
} else{
if(cnt & 1) {
ans += x[i].fi - last;
}
--cnt;
}
last = x[i].fi;
}
++tot;
cout << "Case #" << tot << ": ";
printf("%d\n", ans);
}
return 0;
}
C. Triple
题意:
给出三个数组\(A,B,C\),要求满足条件的三元组\((i,j,k)\),满足:
\[ \left\{ \begin{aligned} |A_i-B_j|\leq C_k\\ |B_j-C_k|\leq A_i\\ |A_i-C_k|\leq B_j \end{aligned} \right. \]
限制条件:\(n\leq 10^5,1\leq A_i,B_i,C_i\leq 10^5\)。
思路:
- 数据范围较小时,可以考虑枚举一个\(i\)作为最大值,然后其余两个数组双指针来扫,时间复杂度\(O(3*n^2)\)。
- 但这对于本题数据来说,显然会\(T\)的,考虑如何优化。
- 注意到在多项式乘法当中,两项相乘其指数相加,那么我们可以将数组对应挂在指数上面,系数则为对应指数出现次数,那么将两个多项式乘起来最终得到一项\(x^n\),其前面的系数则为两数相加和为\(n\)的方案数。
- 此过程\(FFT\)优化即可,时间复杂度\(O(nlogn)\)。
因为本题数据比较特殊,所以就小范围暴力,大范围\(FFT\)优化即可。
另外还注意一些细节,去重的问题:
- 如果出现多个都为最大值的情况,会重复计算。
- 小范围暴力时,可以参考我的代码,只是有些地方限制一下即可。
- \(FFT\)处理时,将三个数组丢到同一个数组当中进行排序,然后扫一遍,对于第\(i\)个位置,假设目前其为最大值。那么我们减掉一下情况就行(以\(a_i\)为最大值为例):
- \(b_j\geq a_i,c_k\geq a_i\)
- \(b_j\geq a_i,c_k<a_i\)
- \(b_j<a_i,c_k\geq a_i\)
- 所以用个桶维护每种数出现次数即可。
详见代码(有点复杂...)
Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
//#define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 3e5 + 5;
int UP = 200000;
const double pi = acos(-1.0);
struct Com{
double x, y;
Com(double xx = 0, double yy = 0) {x = xx, y = yy;}
}a[N], b[N], c[N];
Com operator + (const Com &a, const Com &b) {return Com(a.x + b.x, a.y + b.y);}
Com operator - (const Com &a, const Com &b) {return Com(a.x - b.x, a.y - b.y);}
Com operator * (const Com &a, const Com &b) {return Com(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);}
int l, r[N];
int lim = 1;
void solve(Com *A, int type) {
for(int i = 0; i < lim; i++) if(i < r[i]) swap(A[i], A[r[i]]);
for(int mid = 1; mid < lim; mid <<= 1) {
Com Wn(cos(pi / mid), type * sin(pi / mid)) ;
for(int R = mid << 1, j = 0; j < lim; j += R) {
Com w(1, 0);
for(int k = 0; k < mid; k++, w = w * Wn) {
Com x = A[j + k], y = w * A[j + mid + k];
A[j + k] = x + y;
A[j + mid + k] = x - y;
}
}
}
}
int n;
void FFT(Com *a, Com *b) {
solve(a, 1); solve(b, 1);
for(int i = 0; i <= lim; i++) c[i] = a[i] * b[i];
solve(c, -1);
for(int i = 0; i <= UP; i++) c[i].x = (c[i].x / lim + 0.5);
}
int arr[3][N];
ll sum[N];
ll gao(int p, int q, int r) {
vector <int> num1(lim + 1), num2(lim + 1);
for(int i = 1; i <= n; i++) {
++num1[arr[p][i]];
++num2[arr[q][i]];
}
for(int i = 0; i <= lim; i++) {
a[i] = Com(num1[i]);
b[i] = Com(num2[i]);
}
FFT(a, b);
for(int i = UP; i >= 0; i--)
sum[i] = sum[i + 1] + (ll)c[i].x;
ll ans = 0;
for(int i = 1; i <= n; i++) {
ans += sum[arr[r][i]];
}
return ans;
}
pii tmp[N];
int cnt[3];
ll calc(int id){
ll ans = 0;
int x = (id + 1) % 3;
int y = (x + 1) % 3;
ans += 1ll * cnt[x] * (n - cnt[y]);
ans += 1ll * cnt[y] * (n - cnt[x]);
ans += 1ll * (n - cnt[x]) * (n - cnt[y]);
return ans;
}
ll work2() {
int tot = 0;
for(int i = 0; i < 3; i++) {
for(int j = 1; j <= n; j++) {
cin >> arr[i][j];
tmp[++tot] = MP(arr[i][j], i);
}
}
ll ans = 0;
ans += gao(0, 1, 2);
ans += gao(1, 2, 0);
ans += gao(0, 2, 1);
cnt[0] = cnt[1] = cnt[2] = 0;
sort(tmp + 1, tmp + tot + 1);
for(int i = 1; i <= tot; i++) {
ans -= calc(tmp[i].se);
++cnt[tmp[i].se];
}
return ans;
}
namespace bf {
int getans(int *a, int *b, int *c, int d, int e) {
int ans = 0;
for(int i = 1; i <= n; i++) {
int k = upper_bound(c + 1, c + n + 1, a[i] - e) - c - 1;
int p = k;
for(int j = 1; j <= n && b[j] <= a[i] - d; j++) {
while(k && b[j] + c[k] >= a[i]) --k;
ans += p - k;
}
}
return ans;
}
int A[N], B[N], C[N];
int work() {
for(int i = 1; i <= n; i++) cin >> A[i];
for(int i = 1; i <= n; i++) cin >> B[i];
for(int i = 1; i <= n; i++) cin >> C[i];
sort(A + 1, A + n + 1);
sort(B + 1, B + n + 1);
sort(C + 1, C + n + 1);
int ans = 0;
ans += getans(A, B, C, 0, 0);
ans += getans(B, A, C, 1, 0);
ans += getans(C, A, B, 1, 1);
return ans;
}
}
int Case;
void run() {
++Case;
cin >> n;
ll ans;
if(n <= 2000) {
ans = bf :: work();
} else {
ans = work2();
}
cout << "Case #" << Case << ": ";
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
#ifdef Local
freopen("../input.in", "r", stdin);
freopen("../output.out", "w", stdout);
#endif
while(lim <= UP) lim <<= 1, l++;
for(int i = 0; i < lim; i++) {
r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
}
int t; cin >> t;
while(t--) run();
return 0;
}
D. Counting Sequences I
题意:
定义一个好的序列\(a_1,a_2,\cdots,a_n\)满足:\(n\geq 2\)且\(a_1+a_2+\cdots+a_n=a_1\cdot a_2\cdot \cdots a_n\)。
给定一个长度\(n\),问有多少长度为\(n\)的序列满足是好的序列。
思路:
- 注意到\(1\)在等式里面比较特殊,因为不断增加\(1\)的个数,那么等式左边会不断增加,而等式右边则会不变。
- 因为题目给出的\(n\)较小,所以直接爆搜即可,但要注意剪枝:两边的差已经不能用\(1\)弥补时就及时中止。
代码中爆搜的时候并没有考虑\(1\),而是默认用\(1\)来补差值。同时还要维护一些分母上的东西用来统计答案。
详见代码吧:
Code
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
const int MAXN = 3e3+5,MAXM = 2e6+5,MOD = 1e9+7,INF = 0x3f3f3f3f;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const db eps = 1e-9;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define pii pair<int,int>
#define vii vector<pii>
#define vi vector<int>
using namespace std;
int t,n,ans,a;
ll fact[MAXN],inv[MAXN];
ll qpow(ll a,ll b){
ll ans=1;
for(;b;b>>=1,a=a*a%MOD)if(b&1)ans=ans*a%MOD;
return ans;
}
inline void add(int &x,int y){
x+=y;
if(x>=MOD)x-=MOD;
}
void dfs(int p,ll A,ll B,int pre,ll sum,int cnt){
if(p>n){
if(A==B){
sum=sum*inv[cnt]%MOD;
ans += fact[p-1]*sum%MOD;
ans%=MOD;
}
return ;
}else if(p>1 && A-B == n-p+1){
sum=sum*inv[cnt]%MOD*inv[n-p+1]%MOD;
ans += fact[n]*sum%MOD;
ans%=MOD;
}
for(int i=pre;i<=3000;i++){
if((ll)A*i - (B+i)>n)break;
if(i==pre) dfs(p+1,A*i,B+i,i,sum,cnt+1);
else dfs(p+1,A*i,B+i,i,sum*inv[cnt]%MOD,1);
}
}
int main(){
ios::sync_with_stdio(false);
fact[0]=1;
for(int i=1;i<=3000;i++)fact[i]=fact[i-1]*i%MOD;
inv[3000] = qpow(fact[3000],MOD-2);
for(int i=3000-1;i>=0;i--)inv[i]=inv[i+1]*(i+1)%MOD;
cin>>t;
while(t--){
cin>>n;
ans=0;
dfs(1,1,0,2,1,0);
cout<<ans<<'\n';
}
return 0;
}
E. Counting Sequences II
题意:
给出限制条件\(n,m\),构造序列\(a_i\),满足\(1\leq a_i\leq m\)对于\(1\leq i\leq n\),并满足偶数的个数为偶数。
思路:
- 对于一个偶数,其出现次数可能为\(0,2,4,\cdots\);对于一个奇数,其出现次数可能为\(0,1,2,3,\cdots\)。
- 因为所求情况数为排列,考虑指数生成函数,我们将每个数可能出现的次数挂到指数上,系数则为方案数。
- 易知对于一个偶数,其生成函数为:\(1+\frac{x^2}{2!}+\frac{x^4}{4!}+\cdots\);对于一个奇数,其生成函数为\(1+x+\frac{x^2}{2!}+\frac{x^3}{3!}+\frac{x^4}{4!}+\cdots\)。
- 因为小于等于\(m\)的数有\(\lfloor\frac{m}{2}\rfloor\),记其为\(t\),那么答案就为\((\frac{e^x+e^{-x}}{2})^te^{x(m-t)}\)的第\(n\)项的系数乘以\(n!\)。
- 最后随便化一下,把\((\frac{e^x+e^{-x}}{2})^t\)展开,就得到:\(\frac{1}{2^t}\sum_{i=0}^t {t\choose i}e^{(m-2i)x}\),其第\(n\)项系数为\(\frac{1}{2^t}\sum_{i=0}^t (m-2i)^n\)。
预处理一下阶乘和逆元就行了。
总结一下,关键还是第一步构造生成函数的思想,对每个数独立考虑所有的情况,其余的都很好推。
生成函数nb!多项式nb!
Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
//#define Local
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 2e5 + 5, MOD = 1e9 + 7;
ll qpow(ll a, ll b) {
ll ans = 1;
while(b) {
if(b & 1) ans = ans * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return ans;
}
ll n, m;
int fac[N], inv[N];
int C(ll n, ll m) {
return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}
void init() {
fac[0] = 1;
for(int i = 1; i < N; i++) fac[i] = 1ll * fac[i - 1] * i % MOD;
inv[N - 1] = qpow(fac[N - 1], MOD - 2);
for(int i = N - 2; i >= 0; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
}
void run() {
cin >> n >> m;
int t = m >> 1;
ll ans = 0;
int fm = qpow(qpow(2, t), MOD - 2);
for(int i = 0; i <= t; i++) {
ans = (ans + 1ll * C(t, i) * qpow((m - 2 * i), n) % MOD) % MOD;
}
if(ans < 0) ans += MOD;
ans = ans * fm % MOD;
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
#ifdef Local
freopen("../input.in", "r", stdin);
freopen("../output.out", "w", stdout);
#endif
init();
int t; cin >> t;
while(t--) run();
return 0;
}
F. Rhyme scheme
题意:
求字典序第\(k\)大的序列,该序列满足每一位不会大于前面的最大位加一。
思路:
- 因为是字典序第\(k\)小,所以考虑逐位确定,但是每当我们考虑某一位时我们需要知道后缀个数信息。
- 考虑\(dp\)(递推)(
我也不知道是什么)来计算这个信息。 - 显然每一位与之相关的有当前位数和前面最大的位数,同时由于与后缀相关,我们考虑从后往前依次考虑不同长度的后缀。
- 设\(dp[i,j]\)表示长度为\(n\)的后缀,当前从后往前第\(i\)位,前面最大值为\(j\)时的方案数,那么就有转移方程:
\[ dp[i][j]=dp[i+1][j]*j+dp[i+1][j+1] \]
之后逐位确定就行。(思路与代码不符,详细可以参见传送门)
Code
#include<cstdio>
#include<cmath>
#include<cctype>
#include<algorithm>
#include<vector>
#include<cstring>
#include<cassert>
using namespace std;
#define REP(r,x,y) for(register int r=(x); r<y; r++)
#define REPE(r,x,y) for(register int r=(x); r<=y; r++)
#ifdef sahdsg
#define DBG(...) printf(__VA_ARGS__)
#else
#define DBG(...) (void)0
#endif
template <class T>
inline void read(T&x) {
static int si=1; static char ch; x=0;
do ch=getchar(); while(!isdigit(ch) && ch!='-');
if(ch=='-') si=-1, ch=getchar();
while(isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();} x*=si;
}
template <class T, class... A> inline void read(T&t, A&...a){read(t); read(a...);}
struct Istream {
template <class T>
Istream &operator >>(T &x) {
static char ch;static bool neg;
for(ch=neg=0;ch<'0' || '9'<ch;neg|=ch=='-',ch=getchar());
for(x=0;'0'<=ch && ch<='9';(x*=10)+=ch-'0',ch=getchar());
x=neg?-x:x;
return *this;
}
}fin;
struct Ostream {
template <class T>
Ostream &operator <<(T x) {
x<0 && (putchar('-'),x=-x);
static char stack[233];static int top;
for(top=0;x;stack[++top]=x%10+'0',x/=10);
for(top==0 && (stack[top=1]='0');top;putchar(stack[top--]));
return *this;
}
Ostream &operator <<(char ch) {
putchar(ch);
return *this;
}
}fout;
#define MAXN 1000007
#define LL __int128
int n;
LL f[30][30][30];
LL k;
inline void db() {
memset(f,0,sizeof f);
for(int l=26;l>=1;l--) {
REPE(i,1,26) f[i][1][l]=1;
REPE(j,2,26) {
REPE(i,1,26) {
REPE(k,1,max(i+1,l)) {
f[i][j][l]+=f[k][j-1][max(i+1,l)];
}
}
}
}
}
char ans[30];
inline void calc(int x, int d, LL sum) {
if(d==0) return;
REPE(i,1,x+1) {
sum+=f[max(i,x)][d][x];
if(sum>=k) {
ans[d]=i+'A'-1;
putchar(ans[d]);
calc(max(i,x),d-1,sum-f[max(i,x)][d][x]);
return;
}
}
}
int main() {
db(); DBG("%d\n", f[1][2][9]);
int kase=0;
int T; read(T);
while(0<T--) {
read(n); fin>>k;
printf("Case #%d: ", ++kase);
calc(1,n,0);
putchar('\n');
}
return 0;
}
G. Substring
题意:
现定义两个字符串相等:
- \(S_1=T_1,S_n=T_n\);
- 每个字母出现次数相等。
现在给出串\(S\),然后\(n\)个串,对于每个串,回答它与多少\(S\)的子串相等。串的长度之和不超过\(10^5\)。
思路:
- 首先思考如何判断两个串相等,因为与顺序无关,所以我们可以换一下哈希的方法,使得哈希值只与字母以及出现次数有关。
- 具体地说就是\(hash[i]=hash[i-1]+p[s[i]-'a']\),\(p[k]=p^k\)。
- 因为\(n\)可能很大,对于每个串直接回答会超时。如果只有一个询问的话,因为长度固定,所以可以考虑类似于滑动窗口那样,那么对于一种长度\(O(n)\)就可以求解。
- 注意到虽然有多组询问,但是询问串的长度总和不超过\(10^5\),也就是说长度不同的串为\(\sqrt{10^5}\)个,考虑时限比较宽松,那么直接枚举长度搞就行。
我实现是用了两个unordered_map,最后两个相减得到答案,详细见代码吧:
Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
//#define Local
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int N = 1e5 + 5;
unordered_map <ull, int> mp[26][26], mp2[26][26];
char s[N];
int n;
ull p[26];
int ans[N], tmp[N];
void init() {
p[0] = 131;
for(int i = 1; i < 26; i++) p[i] = p[i - 1] * (ull)131;
}
ull Hash(const string &s) {
int len = s.length();
ull ans = 0;
for(int i = 0; i < len; i++) {
ans += p[s[i] - 'a'];
}
return ans;
}
void gao(int x) {
int len = strlen(s + 1);
ull val = 0;
if(len < x) return;
for(int i = 1; i <= x; i++) {
val += p[s[i] - 'a'];
}
if(mp2[s[1] - 'a'][s[x] - 'a'].count(val)) ++mp[s[1] - 'a'][s[x] - 'a'][val];
for(int i = x + 1; i <= len; i++) {
val += p[s[i] - 'a'];
val -= p[s[i - x] - 'a'];
if(mp2[s[i - x + 1] - 'a'][s[i] - 'a'].count(val)) ++mp[s[i - x + 1] - 'a'][s[i] - 'a'][val];
}
}
void run() {
cin >> s + 1;
cin >> n;
string t;
vector <pair<string, int> > q;
for(int i = 0; i < 26; i++) {
for(int j = 0; j < 26; j++) {
mp[i][j].clear();
mp2[i][j].clear();
}
}
for(int i = 1; i <= n; i++) {
cin >> t;
int lent = t.length();
int fir = t[0] - 'a', last = t[lent - 1] - 'a';
++mp[fir][last][Hash(t)];
++mp2[fir][last][Hash(t)];
tmp[i] = lent;
q.push_back(MP(t, i));
}
sort(tmp + 1, tmp + n + 1);
tmp[n + 1] = 0;
for(int i = 1, j = 1; i <= n; i = j) {
while(tmp[i] == tmp[j]) ++j;
gao(tmp[i]);
}
for(auto &it : q) {
string t = it.fi;
int id = it.se;
int lent = t.length();
ull val = Hash(t);
ans[id] = mp[t[0] - 'a'][t[lent - 1] - 'a'][val] - mp2[t[0] - 'a'][t[lent - 1] - 'a'][val];
}
for(int i = 1; i <= n; i++) cout << ans[i] << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
#ifdef Local
freopen("../input.in", "r", stdin);
freopen("../output.out", "w", stdout);
#endif
init();
int t; cin >> t;
while(t--) run();
return 0;
}
J. Stone game
题意:
现在有\(n\)个石头,每个石头都有权值\(a_i\)。
现在要拿石子,直到满足下面的条件:目前手中石子的权值和大于等于剩下石子的权值和,并且拿掉任意一块石子,手中石子的权值和就小于剩下石子的权值和了。
问这样拿石子的方案有多少种。
思路:
- 注意到最终去掉的石子一定为权值最小的一块,那么我们可以考虑枚举最小权值的石子,然后再来\(dp\)。
- 定义\(dp[i][j]\)表示前\(i\)个石子,权值和为\(j\)的方案数,那么考虑当前石子拿或者不拿直接\(dp\)就行了,很简单的计数\(dp\)。
- 但是这样搞复杂度是\(O(n^3)\)的,显然不能接受。
- 发现我们直接这样搞会计算很多重复的部分,比如前面的很多东西都会重复计算,并且我们每次计算的其实是一个后缀。那么直接将\(dp\)改为从后往前\(dp\)就行了:最小权值每减少一次,\(dp\)就会多计算一个,这样复杂度就为\(O(n^2)\)了。
注意最后要特判一下最后一个位置。
Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 305, MOD = 1e9 + 7, MAX = 150005;
int T;
int n, m;
int a[N], sum[N];
int dp[2][MAX];
void add(int &x, int y) {
x += y;
if(x >= MOD) x -= MOD;
}
int main() {
// freopen("input.in", "r", stdin);
ios::sync_with_stdio(false); cin.tie(0);
scanf("%d", &T);
while(T--) {
sum[0] = 0;
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
sort(a + 1, a + n + 1);
for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i];
int ans = 0;
for(int i = 0; i <= 1; i++) {
for(int j = 0; j <= sum[n]; j++)
dp[i][j] = 0;
}
dp[0][0] = 1;
for(int i = n, cur = 1; i >= 2; i--, cur ^= 1) {
for(int k = 0; k <= sum[n]; k++) dp[cur][k] = dp[cur ^ 1][k];
for(int k = 0; k <= sum[n] - sum[i - 1]; k++) {
if(k >= a[i]) add(dp[cur][k], dp[cur ^ 1][k - a[i]]);
}
for(int k = 0; k <= sum[n] - sum[i - 1]; k++) {
// cout << i << ' ' << k << ' ' << dp[cur][k] << '\n';
if(2 * (k + a[i - 1]) >= sum[n] && 2 * k + a[i - 1] <= sum[n])
add(ans, dp[cur][k]);
}
// for(int k = 0; k <= sum[n] - sum[i - 1]; k++) dp[cur ^ 1][k] = dp[cur][k];
}
if(2 * a[n] >= sum[n] && a[n] <= sum[n]) ++ans;
printf("%d\n", ans);
}
return 0;
}
L. Digit sum
数位\(dp\)板子,或者直接暴力==
Code
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
const int MAXN = 1e6+5,MAXM = 2e6+5,MOD = 998244353,INF = 0x3f3f3f3f;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const db eps = 1e-9;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define pii pair<int,int>
#define vii vector<pii>
#define vi vector<int>
using namespace std;
int t,n,b;
ll f[11][33][11],base[11][22];
void init(){
for(int b=2;b<=10;b++){
base[b][0]=1;
for(int i=1;i<=21;i++)base[b][i]=INF;
for(int i=1;i<22;i++){
base[b][i] = base[b][i-1]*b;
if(base[b][i]>=1000000)break;
}
}
for(int b=2;b<=10;b++){
for(int i=1;base[b][i-1]<=1000000;i++){
for(int j=1;j<b;j++){
f[b][i][j] = f[b][i][0] + j*base[b][i-1];
}
for(int j=0;j<b;j++)f[b][i+1][0] += f[b][i][j];
}
}
}
int a[22];
ll solve(int n,int b){
int cnt=0,tmp=n;
while(tmp){
a[++cnt] = tmp%b;
tmp/=b;
}
ll ans=0;
for(int i=cnt;i>=1;i--){
for(int j=0;j<a[i];j++){
ans += f[b][i][j];
}
n -= a[i]*base[b][i-1];
ans += a[i]*(n+1);
}
return ans;
}
int main(){
ios::sync_with_stdio(false);
init();
cin>>t;
for(int kase=1;kase<=t;kase++){
cin>>n>>b;
cout<<"Case #"<<kase<<": "<<solve(n,b)<<'\n';
}
return 0;
}