这次考炸了,T1其实冷静画画图就可以看出来的,为什么没有看出来呢?
每次都妄想能靠脑子直接看出答案,极其不会利用草稿纸,一定要好好反思,尝试着用各种角度看问题,各种方法在草稿纸上乱玩样例,这样才能够灵感乍现啊
妄图直接靠眼睛看出答案我未免也太高估自己智商了hh
T1
注意到题目中给定的性质:按照给定的字符串建造一棵
T
r
i
e
Trie
Trie树,然后按照任意一种
d
f
s
dfs
dfs序输出,当前序列一定成立。
(对上文的感性解释:按照
T
r
i
e
Trie
Trie树的
d
f
s
dfs
dfs序输出,一定是先把公共前缀走完再输出分别的序列,先走完的与后面构成的前缀长度一定
≤
\le
≤后走的与后面构成的前缀长度)
然后继续对此性质进行研究:发现一个更为有趣的性质——直接将字符串排序即可满足题目要求
于是:
s
t
r
i
n
g
string
string读入,记录原来编号,
s
o
r
t
sort
sort排序,排序后
f
o
r
for
for循环交换,记录交换后编号即可
string和结构体真是慢到令人窒息
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
pair<int,int> sw[N];
struct node{int id;string ch;}per[N];
int n,cnt=0,rk[N];
bool cmp(node a,node b){return a.ch<b.ch;}
int main(){
cin>>n;for(int i=1;i<=n;i++){cin>>per[i].ch;per[i].id=i;}
sort(per+1,per+1+n,cmp);
for(int i=1;i<=n;i++){rk[per[i].id]=i;}
for(int i=1;i<=n;i++){if(per[i].id!=i){sw[++cnt]=make_pair(per[i].id,i);swap(rk[i],rk[per[i].id]);swap(per[i].id,per[rk[per[i].id]].id);}}
printf("%d\n",cnt);for(int i=1;i<=cnt;i++){printf("%d %d\n",sw[i].first,sw[i].second);}
return 0;
}
T2(可持久化线段树)
思路:二分答案,二分当前可达的“最大的最小
t
i
t_i
ti"为
v
v
v,列出当前为达到此二分状态所需要的
s
s
s值,可以后缀和维护
t
i
t_i
ti计算出需要的
s
s
s值,将
v
v
v提出来计算,用可持久化线段树维护当前在
%
x
\%x
%x的意义下有多少个
t
i
t_i
ti严格大于
v
v
v即可
按
t
i
t_i
ti递增排序,维护后缀和,维护二分后有多少个
t
i
t_i
ti严格大于
v
v
v。
注:因为是后缀和,所以计算的都是
∑
\sum
∑起来的值
T3(计数类问题)
CF520E Pluses everywhere
注意到根据加号放置位置与数的关系
讨论加号的位置(一共有
n
−
1
n-1
n−1个位置可以放置加号)
- 这个数在序列中段
考虑在这个数两端添上加号,此时还剩 k − 2 k-2 k−2个加号可以填,还剩可以填的位置有 n − l e n − 2 n-len-2 n−len−2个 ( n − 1 − ( l e n − 1 ) − 2 ) (n-1-(len-1)-2) (n−1−(len−1)−2),其中 l e n − 1 len-1 len−1为当前数长度占了的可以填加号的位置, n − 1 n-1 n−1为本来可以填的位置, − 2 -2 −2为本身钦定要填的加号。 - 这个数在序列头
强制在数的末尾添上一个加号,还剩可填加号为 ( k − 1 ) (k-1) (k−1),可填位置为 ( n − 2 ) (n-2) (n−2),被占用位置为 ( r − 1 ) (r-1) (r−1) - 这个数在序列尾
强制在这个数之前填上一个加号,还剩可填加号为 ( k − 1 ) (k-1) (k−1),可填位置为 ( l − 1 ) (l-1) (l−1),被占用位置为 1 1 1
强制钦定当前加号位置后,考虑所填加号位置对答案的大小的影响:
- 在序列中段
第 i i i位数,在当前数中作为第 j j j个位置出现:
在 a i a_i ai后第1个位置,此时数对答案的贡献为 当 前 数 ∗ 1 0 0 ∗ 可 填 加 号 位 置 的 方 案 数 当前数*10^0*可填加号位置的方案数 当前数∗100∗可填加号位置的方案数;
在 a i a_i ai后第2个位置,此时数对答案的贡献为 当 前 数 ∗ 1 0 1 ∗ 可 填 加 号 位 置 的 方 案 数 当前数*10^1*可填加号位置的方案数 当前数∗101∗可填加号位置的方案数;
在 a i a_i ai后第3个位置,此时数对答案的贡献为 当 前 数 ∗ 1 0 2 ∗ 可 填 加 号 位 置 的 方 案 数 当前数*10^2*可填加号位置的方案数 当前数∗102∗可填加号位置的方案数;
… … …… ……
表示出来为 f i ∗ 1 0 j − 1 ∗ C n − l e n − 2 k − 2 f_i*10^{j-1}*C^{k-2}_{n-len-2} fi∗10j−1∗Cn−len−2k−2 - 在序列首
f i ∗ 1 0 j ∗ C n − r − 1 k − 1 f_i*10^j*C^{k-1}_{n-r-1} fi∗10j∗Cn−r−1k−1 - 在序列末
f i ∗ 1 0 j ∗ C l − 2 k − 1 f_i*10^j*C^{k-1}_{l-2} fi∗10j∗Cl−2k−1
f
i
f_i
fi为当前方案下所有长度为
i
i
i的数的大小,
s
u
m
sum
sum预处理前缀和,
v
a
l
val
val为单个数字的后缀和贡献,
c
a
l
c
calc
calc:相当于求出来当前
[
l
,
r
]
[l,r]
[l,r]之间的和
预处理出当前数
f
i
f_i
fi是多少
假设当前序列为
a
b
c
d
e
abcde
abcde
i
=
=
1
i==1
i==1时
f
i
=
a
+
b
+
c
+
d
+
e
f_i=a+b+c+d+e
fi=a+b+c+d+e
i
=
=
2
i==2
i==2时
f
i
=
a
b
+
b
c
+
c
d
+
d
e
=
10
∗
(
a
+
b
+
c
+
d
)
+
(
b
+
c
+
d
+
e
)
f_i=ab+bc+cd+de=10*(a+b+c+d)+(b+c+d+e)
fi=ab+bc+cd+de=10∗(a+b+c+d)+(b+c+d+e)
i
=
=
3
i==3
i==3时
f
i
=
a
b
c
+
b
c
d
+
c
d
e
=
10
∗
(
a
b
+
b
c
+
c
d
)
+
(
c
+
d
+
e
)
f_i=abc+bcd+cde=10*(ab+bc+cd)+(c+d+e)
fi=abc+bcd+cde=10∗(ab+bc+cd)+(c+d+e)
即
f
i
−
1
∗
10
+
当
前
长
度
的
后
缀
和
f_{i-1}*10+当前长度的后缀和
fi−1∗10+当前长度的后缀和
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;
const int Mod=998244353;
inline int read(){
int cnt=0,f=1;char ch=0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
int fac[N],inv[N],pw[N],sum[N],n;char s[N];
int add(int a,int b){return ((a+b)>=Mod)?(a+b-Mod):(a+b);}
int mul(int a,int b){return 1ll*a%Mod*b%Mod;}
int ksm(int a,int b){int ans=1;for(;b;b>>=1){if(b&1)ans=mul(ans,a);a=mul(a,a);}return ans;}
void prework(){
fac[0]=fac[1]=1;for(int i=2;i<=n;i++){fac[i]=mul(fac[i-1],i);}inv[0]=inv[1]=1;
inv[n]=ksm(fac[n],Mod-2); for(int i=n-1;i>=2;i--){inv[i]=mul(inv[i+1],(i+1));}
pw[0]=1;for(int i=1;i<=n;i++)pw[i]=mul(pw[i-1],10);
}
int calc(int l,int r){return add(sum[r],Mod-mul(sum[l-1],pw[r-l+1]));}
int C(int n,int m){if(n<m||n<0||m<0) return 0;return mul(fac[n],mul(inv[n-m],inv[m]));}
int val[N],f[N],k;
int main(){
n=read(),k=read();scanf("%s",s+1);prework();
for(int i=1;i<=n;i++)sum[i]=add(mul(sum[i-1],10),(s[i]-'0'));
for(int i=n-1;i>=2;i--)val[i]=add(val[i+1],(s[i]-'0'));
f[1]=val[2];
for(int i=2;i<=n-2;i++){
f[i]=mul(10,add(f[i-1],Mod-calc(n-i+1,n-1)));
f[i]=add(f[i],val[i+1]);
}
int ans=0;
for(int i=1;i<=n-2;i++){ans=add(ans,mul(f[i],C(n-i-2,k-2)));}//!!!!!
for(int r=1;r<=n;r++){ans=add(ans,mul(calc(1,r),C(n-r-1,k-1)));}
for(int l=2;l<=n;l++){ans=add(ans,mul(calc(l,n),C(l-2,k-1)));}
printf("%d",ans);
return 0;
}