牛客挑戰賽30 簡要題解

牛客挑戰賽30題解

比賽地址ui

Orz Anson&Deadechospa

A

枚舉\(b,c\),這樣\(a,d\)的限制也就肯定了,二維數點便可。code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')w=0,ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N=505;
int n,a[N],sum[N][N];long long ans;
int getsum(int x1,int x2,int y1,int y2){
    return sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1];
}
int main(){
    n=gi();
    for(int i=1;i<=n;++i)a[i]=gi(),++sum[i][a[i]];
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
    for(int i=2;i<=n;++i)
        for(int j=i+1;j<=n;++j)
            if(a[i]>a[j])
                ans+=getsum(1,i-1,1,a[j]-1)*getsum(j+1,n,a[i]+1,n);
    printf("%lld\n",ans);return 0;
}

B

好像被我強行水過去了?get

考慮一個區間的貢獻\(seed^{(l-1)n+r}\),能夠拆成\(seed^{(l-1)n}\times seed^r\),因此依然能夠考慮枚舉每個數做爲中位數,將大於它的視做\(1\)小於它的視做\(-1\),找到全部包含這個數且和爲\(0\)\(\pm 1\)的區間(由於要考慮中位數有兩個的狀況),將左端點與右端點的權值相乘便可獲得答案。string

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')w=0,ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
#define ll long long
const int N=10005;
const int mod=1e9+7;
int n,seed,a[N],p[N],P[N],tl[N<<1],tr[N<<1],ans;
inline void add(int &x,int y){x+=y;x>=mod?x-=mod:x;}
int main(){
    n=gi();seed=gi();
    for(int i=1;i<=n;++i)a[i]=gi();
    p[0]=1;p[1]=seed;
    for(int i=2;i<=n;++i)p[i]=1ll*p[i-1]*p[1]%mod;
    P[0]=1;P[1]=p[n];
    for(int i=2;i<=n;++i)P[i]=1ll*P[i-1]*P[1]%mod;
    for(int i=1;i<=n;++i){
        memset(tl,0,sizeof(tl));
        memset(tr,0,sizeof(tr));
        int res=0;
        for(int j=i-1,s=0;j>=1;--j)s+=(a[j]>a[i]?1:-1),add(tl[s+N],P[j-1]);
        for(int j=i+1,s=0;j<=n;++j)s+=(a[j]>a[i]?1:-1),add(tr[s+N],p[j]);
        for(int x=min(i-1,n-i)+1,j=-x;j<=x;++j)
            res=(res+((ll)tl[j+N]+tl[j+N]+tl[j-1+N]+tl[j+1+N])*tr[-j+N])%mod;
        res=(res+((ll)tr[N]+tr[N]+tr[1+N]+tr[-1+N])*P[i-1])%mod;
        res=(res+((ll)tl[N]+tl[N]+tl[1+N]+tl[-1+N])*p[i])%mod;
        res=(res+(ll)P[i-1]*p[i]*2)%mod;
        ans=(ans+(ll)res*a[i])%mod;
    }
    printf("%d\n",ans);
    return 0;
}

C

考慮強制一個點最後一個刪,將其做爲根節點,這樣刪點的順序就必定是從葉子到根,於是能夠作一個簡單\(dp\)求出方案數,合併子樹時方案數乘上組合數便可。it

如今要求以每一個點爲根(每一個點最後一個刪)的答案。直接換根\(dp\)便可。io

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')w=0,ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N=1e5+5;
const int mod=998244353;
int n,inv[N],jc[N],jcn[N],sz[N],dp[N],ans;vector<int>E[N];
int fastpow(int a,int b){
    int res=1;
    while(b){if(b&1)res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}
    return res;
}
int C(int n,int m){return 1ll*jc[n]*jcn[m]%mod*jcn[n-m]%mod;}
void work(int u,int v){
    sz[u]+=sz[v],dp[u]=1ll*dp[u]*dp[v]%mod*C(sz[u]-1,sz[v])%mod;
}
void rework(int u,int v){
    dp[u]=1ll*dp[u]*fastpow(1ll*dp[v]*C(sz[u]-1,sz[v])%mod,mod-2)%mod,sz[u]-=sz[v];
}
void dfs1(int u,int f){
    sz[u]=dp[u]=1;
    for(int v:E[u])if(v!=f)dfs1(v,u),work(u,v);
}
void dfs2(int u,int f){
    ans=(ans+dp[u])%mod;
    for(int v:E[u])if(v!=f)rework(u,v),work(v,u),dfs2(v,u),rework(v,u),work(u,v);
}
int main(){
    n=gi();inv[1]=jc[0]=jcn[0]=1;
    for(int i=2;i<=n;++i)inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
    for(int i=1;i<=n;++i)jc[i]=1ll*jc[i-1]*i%mod,jcn[i]=1ll*jcn[i-1]*inv[i]%mod;
    for(int i=1;i<n;++i){
        int u=gi(),v=gi();
        E[u].push_back(v);E[v].push_back(u);
    }
    dfs1(1,0);dfs2(1,0);printf("%d\n",ans);return 0;
}

D

不難發現答案式爲\(\sum_{i=l}^r\binom{i+n-1}{n-1}\binom{s-i+m}{m}\)ast

這是一個\(n+m\)次多項式然而沒辦法在\(O(n+m)\)的時間裏算出前\(n+m\)項。class

考慮其組合意義。至關於從\((0,0)\)點出發走到\((s,n+m)\),每步能夠向上走或是向右走,且保證第\(n\)步向上走時橫座標在\([l,r]\)內的方案數。test

這個等價於求第\(l\)步向右走時縱座標在\([0,n-1]\)的方案數減去第\(r+1\)步向右走時縱座標在\([0,n-1]\)的方案數,而這些是能夠作到\(O(n+m)\)計算的。

#include<cstdio>
#include<algorithm>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')w=0,ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N=2e7+5;
const int mod=998244353;
int n,m,s,l,r,inv[N],f[N],g[N];
int cal(int p){
    if(p>s)return 0;int res=0;
    //\sum_{i=0}^{n-1}\binom{i+p-1}{i}\binom{n+m-1+s-p}{n+m-i}
    for(int i=f[0]=g[0]=1;i<=n+m;++i){
        f[i]=1ll*f[i-1]*(s-p+i)%mod*inv[i]%mod;
        g[i]=1ll*g[i-1]*(p-1+i)%mod*inv[i]%mod;
    }
    for(int i=0;i<n;++i)res=(res+1ll*f[n+m-i]*g[i])%mod;
    return res;
}
int main(){
    n=gi(),m=gi(),s=gi(),l=gi(),r=gi();inv[1]=1;
    for(int i=2;i<N;++i)inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod;
    printf("%d\n",(cal(l)-cal(r+1)+mod)%mod);return 0;
}

E

不難發現題目要求的就是仙人掌上每一條路徑的長度之和。

考慮分別計算每一條邊出如今了多少條路徑中。對仙人掌建圓方樹,而後這個方案數就能夠用簡單換根\(dp\)求出。注意每通過一個方點(一個環)時方案數要乘\(2\)

對於環邊,顯然環上的每一條邊的計算次數都是相同的,因而在\(dfs\)到方點的同時計算一下答案便可。

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')w=0,ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
#define pi pair<int,int>
#define mk make_pair
#define fi first
#define se second
const int N=2e5+5;
int n,tot,m,mod,fa[N],dep[N],dis[N],sum[N],mrk[N],sz[N],ans;
vector<pi>G1[N],G2[N];
void link(int u,int v,int w,vector<pi>*G){
    G[u].push_back(mk(v,w));G[v].push_back(mk(u,w));
}
void dfs(int u,int f){
    fa[u]=f;dep[u]=dep[f]+1;
    for(pi x:G1[u])
        if(x.fi!=f)
            if(!dep[x.fi])dis[x.fi]=(dis[u]+x.se)%mod,dfs(x.fi,u);
            else if(dep[x.fi]>dep[u]){
                sum[++tot]=(x.se+dis[x.first]-dis[u]+mod)%mod;
                for(int v=x.fi;v!=u;v=fa[v])link(tot,v,0,G2),mrk[v]=1;
                link(tot,u,0,G2);
            }
}
void work(int u,int v){
    sz[u]=(sz[u]+(v<=n?1:2)*sz[v])%mod;
}
void undo(int u,int v){
    sz[u]=(sz[u]-(v<=n?1:2)*sz[v]+mod+mod)%mod;
}
void dfs1(int u,int f){
    sz[u]=(u<=n);
    for(pi x:G2[u])if(x.fi!=f)dfs1(x.fi,u),work(u,x.fi);
}
void dfs2(int u,int f){
    if(u>n){
        int res=0,tmp=0;
        for(pi x:G2[u])res=(res+1ll*tmp*sz[x.fi])%mod,tmp=(tmp+sz[x.fi])%mod;
        ans=(ans+1ll*res*sum[u])%mod;
    }
    for(pi x:G2[u])
        if(x.fi!=f){
            undo(u,x.fi);ans=(ans+1ll*sz[u]*sz[x.fi]%mod*x.se)%mod;
            work(x.fi,u);dfs2(x.fi,u);undo(x.fi,u);work(u,x.fi);
        }
}
int main(){
    n=tot=gi();m=gi();mod=gi();
    for(int i=1,x,y,z;i<=m;++i)x=gi(),y=gi(),z=gi(),link(x,y,z,G1);
    dfs(1,0);
    for(int i=2;i<=n;++i)if(!mrk[i])for(pi x:G1[i])if(x.fi==fa[i])link(i,fa[i],x.se,G2);
    dfs1(1,0);dfs2(1,0);printf("%d\n",ans);return 0;
}

F

考慮一下存在二次剩餘的數\(x\)知足一些什麼樣的性質。

\(x^{\frac{p-1}{2}}\equiv1\mod p\)

\(\mod p\)意義下恰有\(\frac{p-1}{2}\)個數存在二次剩餘,即恰有\(\frac{p-1}{2}\)個數知足上式。

根據某些代數高論咱們能夠獲得

\(x^{\frac{p-1}{2}}-1=\prod_{i\mbox{存在二次剩餘}}(x-i)\)

而咱們要求的東西即爲等式右邊的\(k\)次方與\(f(x)\)\(\gcd\)

能夠先求出\((x^{\frac{p-1}{2}}-1)^k\mod f(x)\)後再與\(f(x)\)\(\gcd\)。複雜度\(O(n^2\log p)\)

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')w=0,ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
#define vi vector<int>
const int mod=998244353;
int inv(int a){
    int res=1,b=mod-2;
    while(b){if(b&1)res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}
    return res;
}
void print(vi a){
    int n=a.size();printf("%d\n",n-1);
    for(int i=0;i<n;++i)printf("%d ",a[i]);puts("");
}
vi mul(vi a,vi b){
    int n=a.size(),m=b.size();vi c;c.resize(n+m-1);
    for(int i=0;i<n;++i)
        for(int j=0;j<m;++j)
            c[i+j]=(c[i+j]+1ll*a[i]*b[j])%mod;
    while(c.size()&&!c.back())c.pop_back();return c;
}
vi Mod(vi a,vi b){
    int n=a.size(),m=b.size();
    for(int i=n-1;i>=m-1;--i)
        if(a[i]){
            int t=1ll*(mod-a[i])*inv(b[m-1])%mod;
            for(int j=0;j<m;++j)a[i-j]=(a[i-j]+1ll*b[m-1-j]*t)%mod;
        }
    while(a.size()&&!a.back())a.pop_back();return a;
}
vi fastpow(vi a,int b,vi c){
    vi res;res.push_back(1);
    while(b){
        if(b&1)res=Mod(mul(res,a),c);
        a=Mod(mul(a,a),c);b>>=1;
    }
    return res;
}
vi gcd(vi a,vi b){
    if(!b.size())return a;
    return gcd(b,Mod(a,b));
}
int main(){
    int n=gi(),k=gi();vi f,g;
    for(int i=0;i<=n;++i)f.push_back(gi());
    g.push_back(0);g.push_back(1);
    g=fastpow(g,mod-1>>1,f);
    if(!g.size())g.push_back(mod-1);else g[0]=(g[0]+mod-1)%mod;
    g=fastpow(g,k,f);g=gcd(f,g);
    int t=inv(g[g.size()-1]);
    for(int i=0;i<g.size();++i)g[i]=1ll*g[i]*t%mod;
    print(g);return 0;
}
相關文章
相關標籤/搜索