ARC 杂题
ARC150C Path and Subsequence
考试题削弱版。
给你一个无向图,点 \(i\) 有点权 \(v_i\)。给你一个长为 \(k\) 的序列 \(b\),求 \(1\to n\) 的所有路径是否全部包含子序列 \(b\)。
\(k\le n,m\le 10^5\)
枚举 \(b_i\),从 \(1\) 开始,将所有不经过点权为 \(b_i\) 的点打上标记(时间戳) \(i\),接下来再对没经过的点往下跑。若 \(n\) 的时间戳为 \(k\) 则说明包含了。时间复杂度 \(O(n+m+k)\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=5e5+3;
int n,m,s;
vector<int>e[maxn];
queue<int>q,p;
int vis[maxn];
int c[maxn],b[maxn];
void dfs(int u,int t){
for(int v:e[u]){
if(vis[v]==-1){
vis[v]=t;
if(c[v]!=b[t+1]){
dfs(v,t);
}else{
p.push(v);
}
}
}
}
signed main(){
ios::sync_with_stdio(0);
cin>>n>>m>>s;
for(int i=1,u,v;i<=m;i++){
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
for(int i=1;i<=n;i++) cin>>c[i],vis[i]=-1;
for(int i=1;i<=s;i++) cin>>b[i];
e[0].push_back(1);
vis[0]=0;
q.push(0);
for(int i=0;i<=s;i++){
while(!q.empty()){
int j=q.front();
q.pop();
vis[j]=i;
dfs(j,i);
}
q=p;
while(!p.empty()) p.pop();
}
if(vis[n]==s) cout<<"Yes";
else cout<<"No";
return 0;
}
ARC185E Adjacent GCD
给你一个数列 \(a\),对于每个 \(m\in [1,n]\),求对于每个 \(a_{[1,m]}\) 的子序列 \(S\) 相邻两项的 GCD 的和的和。
\(n\le 5\times 10^5,a_i\le 10^5\)
设 \(f(i)\) 为以 \(i\) 结尾的答案。则 \(f(i)\) 的答案可以由选/不选 \(i\) 继承过来,再计算从每个 \(j<i\) 转移过来的系数即可,即
由于有 \(\gcd(a_i,a_j)=\sum\limits_{d\mid \gcd(a_i,a_j)} \varphi(d)=\sum\limits_{d} [d\mid a_i][d\mid a_j]\varphi(d)\),所以
把与 \(j\) 无关的往前扔
记 \(g(d,i)=\sum\limits_{j<i} 2^{j-1} [d\mid a_j]\),显然这一坨可以 \(O(d(a_i))\) 动态维护,考虑每次加入 \(a_i\) 统计完当前答案后,就在 \(d\mid a_i\) 的位置加上 \(2^{i-1}\) 的贡献即可。
所以最后的式子就是 \(f(i)=2f(i-1)+\sum\limits_{d\mid a_i} \varphi(d) g(d,i)\),时间复杂度 \(O(nd(n))\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=5e5+7;
const int mod=998244353;
int n;
int a[maxn];
int f[maxn],g[maxn],po2[maxn],k[maxn],val[maxn];
vector<int>v[maxn];
signed main(){
cin>>n;
po2[0]=1;
for(int i=1;i<=n;i++){
cin>>a[i];
po2[i]=po2[i-1]*2%mod;
}
for(int i=1;i<=100000;i++){
k[i]+=i; v[i].emplace_back(i);
for(int j=2*i;j<=100000;j+=i){
k[j]-=k[i];
v[j].emplace_back(i);
}
}
for(int x=1;x<=n;x++){
f[x]=2*f[x-1];
for(int d:v[a[x]]){
f[x]=(f[x]+k[d]*val[d]%mod)%mod;
val[d]=(val[d]+po2[x-1])%mod;
}
cout<<f[x]<<'\n';
}
return 0;
}
ARC183C Not Argmax
求满足 \(m\) 个形如【\([l_i,r_i]\) 中最大值的不为 \(p_{x_i}\)】的排列 \(p\) 的数量。
\(n\le 500,m\le 2\times 10^5\)
考虑没有限制时怎么 DP,设 \(f(l,r)\) 表示 \([l,r]\) 的答案,枚举区间内最大值的位置,则有
加上限制即记个标记 \(t(l,r,k)\),当满足标记时不转移即可,注意转移顺序。标记转移为 \(t(l,r,k)|=t(l+1,r,k)|t(l,r-1,k)\)。时间复杂度 \(O(n^3+m)\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=507;
const int maxm=2e5+7;
const int mod=998244353;
int n,m;
int f[maxn][maxn],fac[maxn],ifac[maxn];
bitset<maxn>is[maxn][maxn];
int qpow(int a,int b){
int res=1;
for(;b;b>>=1,a=a*a%mod) if(b&1) res=res*a%mod;
return res;
}
int C(int a,int b){
if(a<b) return 0;
return fac[a]*ifac[a-b]%mod*ifac[b]%mod;
}
signed main(){
cin>>n>>m;
for(int i=1,l,r,x;i<=m;i++){
cin>>l>>r>>x;
is[l][r][x]=1;
if(l==r){
cout<<"0\n";
return 0;
}
}
for(int len=2;len<=n;len++)
for(int l=1;l+len-1<=n;l++){
int r=l+len-1;
is[l][r]|=is[l+1][r];
is[l][r]|=is[l][r-1];
}
fac[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
ifac[n]=qpow(fac[n],mod-2);
for(int i=n-1;~i;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
f[1][0]=1;
for(int i=1;i<=n;i++) f[i][i]=f[i+1][i]=1;
for(int len=2;len<=n;len++)
for(int l=1;l+len-1<=n;l++){
int r=l+len-1;
for(int k=l;k<=r;k++) if(!is[l][r][k])
f[l][r]=(f[l][r]+f[l][k-1]*f[k+1][r]%mod*C(r-l,k-l)%mod)%mod;
}
cout<<f[1][n];
return 0;
}