AtCoder Beginner Contest 399 D,F 题解
D. Switch Seats
题意
有一个长度为 2 N 2N 2N 的序列 A A A,每个 1 1 1 到 N N N 的整数出现两次
找到满足以下要求的 ( a , b ) (a,b) (a,b):
-
1 ≤ a < b ≤ N 1\le a<b \le N 1≤a<b≤N
-
两个 a a a 在 A A A 中的位置不相邻,两个 b b b 在 A A A 中的位置不相邻
-
执行以下操作任意多次,使得两个 a a a 相邻,两个 b b b 也相邻:
- 选择一个 a a a,选择一个 b b b,交换它们的位置
多测,共 T T T 组数据
思路
有很多方法,我详细说一下分类讨论:
显然,只有 “ab…ab”“ab…ba”“ba…ab”和"ba…ba" 符合条件
定义以下数组:
p
o
s
i
pos_i
posi 是 pair<int,int>
类型,存储数字
i
i
i 在序列中两次出现的位置
a
d
j
i
adj_i
adji 是 vector<int>
类型,大小为
4
4
4,存储每个数字
i
i
i 相邻的数,下标
0
0
0 表示第一次出现的
i
i
i 左边的 数 ,
1
1
1 表示第一次右边,
2
2
2 表示第二次左边,
3
3
3 表示第二次右边,若没有相邻的数,这一位就是
−
1
-1
−1
由于 a < b a<b a<b,对于每个 i i i,只考虑比它大的数与它组成的数对:
-
若 a d j i , 0 > i adj_{i,0}>i adji,0>i,以下情况符合要求,设 x = a d j i , 0 x=adj_{i,0} x=adji,0:“xi…xi"或"xi…ix”
-
若 a d j i , 1 > i adj_{i,1}>i adji,1>i,设 x = a d j i , 1 x=adj_{i,1} x=adji,1:
-
若 a d j i , 2 = x adj_{i,2}=x adji,2=x
这种情况显然可以:
但还有特殊情况:
-
若另一个 x x x 不在 i 1 i_1 i1 前并且不在 i 2 i_2 i2 后,即 p o s i . f i r s t + 2 = p o s i . s e c o n d pos_i.first+2=pos_i.second posi.first+2=posi.second 时,不可以:
-
若两个 x x x 相邻,即 p o s x . f i r s t + 1 = p o s x . s e c o n d pos_x.first+1=pos_x.second posx.first+1=posx.second 时,不可以:
-
否则可以:
注意:不要计算
xixi
的情况,因为这样就重复了
-
-
若 a d j i , 3 = x adj_{i,3}=x adji,3=x 可以
-
-
其它情况都不可以
C++ 代码
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef pair<int,int> pii;
const int maxn=400005;
int v[maxn];
vector<int> adj[maxn];
pii pos[maxn];
void solve(){
int n;
cin>>n;
//清空
for(int i=1;i<=n;i++){
pos[i].first=pos[i].second=0;
adj[i].clear();
}
for(int i=1;i<=2*n;i++){
cin>>v[i];
if(pos[v[i]].first==0) pos[v[i]].first=i;
else pos[v[i]].second=i;
}
for(int i=1;i<=2*n;i++){
adj[v[i]].pb(v[i-1]==0?-1:v[i-1]);
adj[v[i]].pb(v[i+1]==0?-1:v[i+1]);
}
int ans=0;
set<pii> st;
for(int i=1;i<=n;i++){
if(pos[i].first+1==pos[i].second) continue;//相邻直接跳过
if(adj[i][0]>i){
if(adj[i][2]==adj[i][0]||adj[i][3]==adj[i][0]){
ans++;
}
}
if(adj[i][1]>i){
if(adj[i][1]==adj[i][2]){
if(pos[i].first+2==pos[i].second){
if(adj[i][2]!=adj[i][3]){
//不可以
}else{
ans++;
}
}else if(pos[adj[i][1]].first+1==pos[adj[i][2]].second){
//不可以
}else{
ans++;
}
}else if(adj[i][3]==adj[i][1]){
ans++;
}
}
}
cout<<ans<<endl;
}
int main(){
int32_t T;
cin>>T;
while(T--){
solve();
}
return 0;
}
F. Range Power Sum
题意
给你 N , K N,K N,K 和序列 A A A,求出 ∑ 1 ≤ l ≤ r ≤ N ( ∑ l ≤ i ≤ r A i ) K m o d 998244353 \displaystyle \sum_{1\le l \le r \le N}(\sum_{l\le i\le r}^{A_i})^K \bmod 998244353 1≤l≤r≤N∑(l≤i≤r∑Ai)Kmod998244353 的值.
思路
我们先不管取模
首先,看到了区间求和,不难想到前缀和,即定义 S i = ∑ x = 1 i A x S_i=\sum_{x=1}^i A_x Si=∑x=1iAx
原式转化为 ∑ 1 ≤ l ≤ r ≤ N ( S r − S l − 1 ) K \displaystyle \sum_{1\le l \le r \le N} (S_r-S_{l-1})^K 1≤l≤r≤N∑(Sr−Sl−1)K,但是仍然为 O ( N 2 ) O(N^2) O(N2)
看到了
(
a
±
b
)
n
(a\pm b)^n
(a±b)n,迅速想到二项式定理,其中
(
n
i
)
\displaystyle {n \choose i}
(in) 表示组合数
n
n
n 选
i
\displaystyle i
i =
n
!
i
!
(
n
−
i
)
!
\displaystyle \frac{n!}{i!(n-i)!}
i!(n−i)!n!:
(
a
−
b
)
n
=
∑
i
=
0
n
(
n
i
)
a
i
(
−
b
)
n
−
i
(a-b)^n=\sum_{i=0}^{n} \binom{n}{i}a^i(-b)^{n-i}
(a−b)n=i=0∑n(in)ai(−b)n−i
原式 = ∑ 1 ≤ l ≤ r ≤ N ∑ i = 0 K ( K i ) S r i ( − S l − 1 ) K − i = ∑ l = 1 N ∑ r = l N ∑ i = 0 K ( K i ) S r i ( − S l − 1 ) K − i = ∑ r = 1 N ∑ i = 0 K ( K i ) S r i ( ∑ l = 1 r ( − S l − 1 ) K − i ) \begin{matrix} 原式 &= \displaystyle \sum_{1\le l \le r \le N} \sum_{i=0}^{K} \binom{K}{i} S_r^i(-S_{l-1})^{K-i} \\ &=\displaystyle \sum_{l=1}^N\sum_{r=l}^N \sum_{i=0}^{K} \binom{K}{i} S_r^i(-S_{l-1})^{K-i} \\ &=\displaystyle \sum_{r=1}^N\sum_{i=0}^K \binom{K}{i} S_r^i(\sum_{l=1}^r(-S_{l-1})^{K-i}) \end{matrix} 原式=1≤l≤r≤N∑i=0∑K(iK)Sri(−Sl−1)K−i=l=1∑Nr=l∑Ni=0∑K(iK)Sri(−Sl−1)K−i=r=1∑Ni=0∑K(iK)Sri(l=1∑r(−Sl−1)K−i)
此时,我们发现 ∑ l = 1 r ( − S l − 1 ) K − i \sum_{l=1}^r(-S_{l-1})^{K-i} ∑l=1r(−Sl−1)K−i 是可以预处理的,定义 p x p_x px 初始值为 ∑ l = 1 r ( − S l − 1 ) x \sum_{l=1}^r(-S_{l-1})^x ∑l=1r(−Sl−1)x,对于每个 r r r 的每个 i i i 的操作结束后, p K − i = p K − i − S r − 1 K − i p_{K-i}=p_{K-i}-S_{r-1}^{K-i} pK−i=pK−i−Sr−1K−i, p K − i p_{K-i} pK−i 就变成了 ∑ l = 1 r − 1 ( − S l − 1 ) K − i \sum_{l=1}^{r-1}(-S_{l-1})^{K-i} ∑l=1r−1(−Sl−1)K−i
则原式 = ∑ r = 1 N ∑ i = 0 K ( K i ) S r i ⋅ p K − i \displaystyle \sum_{r=1}^N\sum_{i=0}^K \binom{K}{i} S_r^i·p_{K-i} r=1∑Ni=0∑K(iK)Sri⋅pK−i,时间复杂度 O ( N K ) O(NK) O(NK),可以通过
再来考虑组合数计算,由于 K ≤ 10 K \le 10 K≤10 ,直接计算,无需预处理:
int C(int n,int k){
int res=1;
for(int i=n;i>=k+1;i--) res*=i;
for(int i=1;i<=n-k;i++) res/=i;
return res;
}
接下来考虑取模:
由于 p x p_x px 可能变成负数,而C++自带取模结束后可能为负数(如 − 3 m o d 5 = 2 -3 \bmod 5=2 −3mod5=2,但程序会告诉你 − 3 m o d 5 = − 3 -3 \bmod 5=-3 −3mod5=−3),所以取模过程如下:
p[x]=(p[x]%MOD+MOD)%MOD
最后考虑乘方,由于快速幂比较好写,所以直接用了:
int pw(int a,int b){
int res=1;
while(b){
if(b&1) res=res*a%MOD;
a=a*a%MOD;
b>>=1;
}
return res;
}
C++ 代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MOD=998244353;
const int maxn=200005;
int n,k;
int v[maxn],s[maxn];
int p[15];
int pw(int a,int b){
int res=1;
while(b){
if(b&1) res=res*a%MOD;
a=a*a%MOD;
b>>=1;
}
return res;
}
int C(int n,int k){
int res=1;
for(int i=n;i>=k+1;i--) res*=i;
for(int i=1;i<=n-k;i++) res/=i;
return res;
}
signed main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>v[i];
s[i]=(v[i]+s[i-1])%MOD;
}
for(int i=0;i<=k;i++){
for(int j=0;j<n;j++){
p[i]=(p[i]+pw(-s[j],i))%MOD;
p[i]=(p[i]+MOD)%MOD;
}
}
int ans=0;
for(int r=n;r>=1;r--){
for(int i=0;i<=k;i++){
ans=(ans+C(k,i)*pw(s[r],i)%MOD*p[k-i]%MOD)%MOD;
ans=(ans+MOD)%MOD;
p[k-i]=(p[k-i]-pw(-s[r-1],k-i))%MOD;
p[k-i]=(p[k-i]+MOD)%MOD;
}
}
cout<<ans<<endl;
return 0;
}