Problem A.
发现可以用 x x 次翻转操作替换 x x 次反转操作。做完了。
Problem B.
发现 n≥50 n ≥ 50 时变成等差数列。(实际上是 n≥12 n ≥ 12 )
所以小范围大暴力,然后 O(1) O ( 1 ) 计算。
Problem C.
发现答案的式子为:
−2×∑i=1n(ni)3(n−i)n+i(−1)i−∑i=1n∑j=1n(ni)(nj)3(n−i)(n−j)+1(−1)i+j − 2 × ∑ i = 1 n ( n i ) 3 ( n − i ) n + i ( − 1 ) i − ∑ i = 1 n ∑ j = 1 n ( n i ) ( n j ) 3 ( n − i ) ( n − j ) + 1 ( − 1 ) i + j
第一部分可以大暴力计算,复杂度 O(nlogn) O ( n l o g n ) (其实是 O(1) O ( 1 ) )
第二部分转化如下:
S=3×∑i=1n(ni)(−1)i∑j=1n(nj)(3(n−i))(n−j)(−1)j =3×∑i=1n(ni)(−1)i[(3n−i−1)n−(3n−i)n] S = 3 × ∑ i = 1 n ( n i ) ( − 1 ) i ∑ j = 1 n ( n j ) ( 3 ( n − i ) ) ( n − j ) ( − 1 ) j = 3 × ∑ i = 1 n ( n i ) ( − 1 ) i [ ( 3 n − i − 1 ) n − ( 3 n − i ) n ]
#include
#define maxn 1001000
#define mod 998244353
using namespace std;
typedef long long ll;
int C[maxn],inv[maxn],pw3[maxn],n;
int qpow(ll a,ll b){
int ans=1,tmp=a;
for(;b;b>>=1,a=1ll*a*a%mod)
if(b&1)ans=1ll*ans*a%mod;
return ans;
}
int main(){
scanf("%d",&n);
pw3[0]=1;
for(int i=1;i<=n;++i)pw3[i]=1ll*pw3[i-1]*3ll%mod;
inv[1]=1;
for(int i=2;i<=n;++i)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=0,c=1;i<=n;++i)
C[i]=c,c=1ll*(n-i)*inv[i+1]%mod*c%mod;
int ans=0;
int nw3=qpow(3,1ll*(n-1)*n+1);
int f3=qpow(3,mod-1-(n-1));
for(int i=1;i<=n;++i)
ans=(ans+1ll*C[i]*nw3*(i&1?1:-1))%mod,nw3=1ll*nw3*f3%mod;
ans=2ll*ans%mod;
int sum=0;
for(int i=1;i<=n;++i){
int fac=0;
fac=qpow(pw3[n-i]-1,n)-qpow(pw3[n-i],n);
fac=1ll*fac*C[i]*(i&1?-1:1)%mod;
sum=(sum-fac)%mod;
}
ans=(ans%mod+mod*5ll+3ll*sum%mod)%mod;
printf("%d",ans);
}
Problem D.
发现如果分别对 2 2 颗树求出环长为 0..K 0.. K 的答案,就能 O(K2) O ( K 2 ) 地得到最终答案
令 dp[u][i] d p [ u ] [ i ] 表示从 u u 往子树走 i i 步又回到 u u 的方案数
发现 dp d p 数组的合并是一个完全背包的过程。
于是这可以用 O(nK2) O ( n K 2 ) 得到。
然后再从上往下来一遍,同样是 O(nK2) O ( n K 2 ) 的。
#include
#define maxn 4010
#define mod 998244353
using namespace std;
typedef long long ll;
int inv[maxn],fac[maxn],pre[maxn][80],anses[maxn][80];
int C[80][80],ans1,ans2,n1,n2,K,dp[maxn][80],f[80],g[80];
vector<int>G[maxn];
void upd(int& x,int y){
(x+=y)>=mod?x-=mod:0;
}
void dfs1(int u,int f){
dp[u][0]=1;
for(auto v:G[u])if(v!=f)
dfs1(v,u);
for(int i=0;iif(dp[u][i])
for(auto v:G[u])if(v!=f)
for(int j=0;dp[v][j]&&i+j+1<=K;++j)
dp[u][i+j+1]=(dp[u][i+j+1]+1ll*dp[u][i]*dp[v][j])%mod;
}
void dfs2(int u,int f){
int ls[80]={0},_ls[80]={0};
for(auto v:G[u])if(v!=f)
for(int i=0;i1],dp[v][i]);
for(int i=0;i1],pre[u][i]);
auto cal=[&](int to[]){
for(int i=0;i<=K;++i)to[i]=0,_ls[i]=0;
to[0]=1,_ls[0]=1;
for(int i=1;i<=K;++i)
for(int j=i;j>=1;--j)
to[i]=(to[i]+1ll*to[i-j]*ls[j])%mod;
};
cal(anses[u]);
for(auto v:G[u])if(v!=f){
for(int i=0;i1],mod-dp[v][i]);
cal(pre[v]);
dfs2(v,u);
for(int i=0;i1],dp[v][i]);
}
}
void sol(int n,int f[]){
for(int i=1;i<=n;++i)G[i].clear();
for(int i=1;i<=n;++i)
for(int j=0;j<=K;++j)
dp[i][j]=anses[i][j]=pre[i][j]=0;
for(int i=2,u,v;i<=n;++i){
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs1(1,0);
dfs2(1,0);
for(int i=1;i<=n;++i)
for(int j=0;j<=K;++j)
upd(f[j],anses[i][j]);
}
int qpow(int a,int b){
int ans=1;
for(;b;b>>=1,a=1ll*a*a%mod)
if(b&1)ans=1ll*ans*a%mod;
return ans;
}
int main(){
scanf("%d%d%d",&n1,&n2,&K);
if(K&1)return puts("0"),0;
K>>=1,fac[0]=inv[0]=1;
for(int i=1;i<=K;++i)fac[i]=1ll*fac[i-1]*i%mod;
inv[K]=qpow(fac[K],mod-2);
for(int i=K-1;i>=1;--i)inv[i]=1ll*inv[i+1]*(i+1)%mod;
for(int i=0;i<=K*2;++i)
for(int j=*C[i]=1;j<=i;++j)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
sol(n1,f),sol(n2,g);
int ans=0;
for(int i=0;i<=K;++i)
ans=(ans+1ll*f[i]*g[K-i]%mod*C[2*K][i*2])%mod;
printf("%d",ans);
}
Problem E.
根号是什么垃圾玩意…本题复杂度 O(nlogn) O ( n l o g n )
一个区间 [l,r] [ l , r ] 合法一定有 mx−mn−r+l=0 m x − m n − r + l = 0
然而一个区间 mx−mn−r+l m x − m n − r + l 恒大于等于 0 0
所以就是求最小值出现个数。(因为最小值必为 0 0 )
考虑扫描线时同时维护历史最小值出现次数
一个节点维护 mn,ans,tim,atg,cnt m n , a n s , t i m , a t g , c n t
表示最小值,答案,离上一次到该节点过了多长时间,加标记,最小值个数
每次 pushdown p u s h d o w n 时往最小值所在区间更新 tim t i m
答案就加 tim×cnt t i m × c n t
#include
#define xx first
#define yy second
#define maxn 130000
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const ll inf=1ll<<60;
struct WXHAK{
ll mn,atg,ans,tim,cnt;
}s[maxn<<2];
void add(WXHAK& w,ll x){
w.mn+=x;
w.atg+=x;
}
void addtim(WXHAK& w,ll t){
w.tim+=t;
w.ans+=w.cnt*t;
}
void pd(int o){
int ls=o<<1,rs=o<<1|1;
if(s[o].atg){
add(s[ls],s[o].atg);
add(s[rs],s[o].atg);
s[o].atg=0;
}
if(s[o].tim){
if(s[o].mn==s[ls].mn)addtim(s[ls],s[o].tim);
if(s[o].mn==s[rs].mn)addtim(s[rs],s[o].tim);
s[o].tim=0;
}
}
void upd(int o){
int ls=o<<1,rs=o<<1|1;
s[o].mn=min(s[ls].mn,s[rs].mn);
s[o].ans=s[ls].ans+s[rs].ans;
s[o].cnt=0;
if(s[ls].mn==s[o].mn)s[o].cnt+=s[ls].cnt;
if(s[rs].mn==s[o].mn)s[o].cnt+=s[rs].cnt;
}
void mdy(int o,int l,int r,int ql,int qr,int x){
if(ql<=l&&r<=qr){
add(s[o],x);
return ;
}
pd(o);
int mid=l+r>>1;
if(ql<=mid)mdy(o<<1,l,mid,ql,qr,x);
if(qr>mid)mdy(o<<1|1,mid+1,r,ql,qr,x);
upd(o);
}
ll qry(int o,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr)return s[o].ans;
int mid=l+r>>1;
pd(o);
if(qr<=mid)return qry(o<<1,l,mid,ql,qr);
else if(ql>mid)return qry(o<<1|1,mid+1,r,ql,qr);
return qry(o<<1,l,mid,ql,qr)+qry(o<<1|1,mid+1,r,ql,qr);
}
void build(int o,int l,int r){
if(l==r){
s[o].mn=l;
s[o].cnt=1;
return ;
}
int mid=l+r>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
upd(o);
}
ll anses[maxn];
int n,a[maxn],Q,stmx[maxn],stmn[maxn],tmx,tmn;
vector G[maxn];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
scanf("%d",&Q);
for(int i=1,l,r;i<=Q;++i){
scanf("%d%d",&l,&r);
G[r].push_back(pii(l,i));
}
build(1,1,n);
for(int i=1;i<=n;++i){
mdy(1,1,n,1,n,-1);
while(tmx&&a[stmx[tmx]]1,1,n,stmx[tmx-1]+1,stmx[tmx],-a[stmx[tmx]]),tmx--;
mdy(1,1,n,stmx[tmx]+1,i,a[i]),stmx[++tmx]=i;
while(tmn&&a[stmn[tmn]]>a[i])
mdy(1,1,n,stmn[tmn-1]+1,stmn[tmn],a[stmn[tmn]]),tmn--;
mdy(1,1,n,stmn[tmn]+1,i,-a[i]),stmn[++tmn]=i;
addtim(s[1],1);
for(auto p:G[i])anses[p.second]=qry(1,1,n,p.first,i);
}
for(int i=1;i<=Q;++i)printf("%lld\n",anses[i]);
}