Codeforces 493 Div1题解

Problem A.
发现可以用 x x 次翻转操作替换 x x 次反转操作。做完了。
Problem B.
发现 n50 n ≥ 50 时变成等差数列。(实际上是 n12 n ≥ 12
所以小范围大暴力,然后 O(1) O ( 1 ) 计算。
Problem C.
发现答案的式子为:
2×i=1n(ni)3(ni)n+i(1)ii=1nj=1n(ni)(nj)3(ni)(nj)+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)ij=1n(nj)(3(ni))(nj)(1)j   =3×i=1n(ni)(1)i[(3ni1)n(3ni)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 ] 合法一定有 mxmnr+l=0 m x − m n − r + l = 0
然而一个区间 mxmnr+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;
vectorG[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]);
}

你可能感兴趣的:(比赛题解)