1. 程式人生 > >Comet OJ - Contest #2簡要題解

Comet OJ - Contest #2簡要題解

head typedef 合並 truct void pre fin 二次 維護

Comet OJ - Contest #2簡要題解

前言:

我沒有小裙子,我太菜了。

A 因自過去而至的殘響起舞

https://www.cometoj.com/contest/37/problem/A?problem_id=1528

  • 容易發現那玩意增長的飛快,只要模擬就可以了
//? ayaponzu*
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
using namespace std;
typedef long long ll;
ll n;
ll f[100000];
int main() {
    int i;
    scanf("%lld",&n);
    f[1]=1,f[2]=1;
    ll sum=2;
    if(n==1) {puts("2"); return 0;}
    //if(n==2) {puts("2"); return 0;}
    for(i=3;i<=10000;i++) {
        f[i]=sum/2;
        sum+=f[i];
        if(sum>n) {
            printf("%d\n",i); return 0;
        }
    }
}

B 她的想法、他的戰鬥

https://www.cometoj.com/contest/37/problem/B?problem_id=1529

  • 根據期望的線性性,可知\(E(q)=\frac{L+R}{2}\), 那麽利潤就是\(\frac{p-l}{r-l}(E(q)-p)\)
  • 不難發現這東西是個二次函數,直接求最大值就可以了。
  • 最優的\(p\)不在\([l,r]\)區間時需要特判。
//? ayaponzu*
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
using namespace std;
typedef long long ll;
typedef double f2;
ll n;
ll f[100000];
int main() {
    f2 l,r,L,R;
    scanf("%lf%lf%lf%lf",&l,&r,&L,&R);
    f2 q=(L+R)*0.5;
    f2 x=(q+l)/2;
    if(x<l) {
        puts("0.0000"); return 0;
    }
    f2 ans;
    if(x>r) {
        ans=q-r;
    }
    else ans=(-x*x+(q+l)*x-l*q)/(r-l);
    if(ans<1e-6) ans=0;
    printf("%.4f\n",ans);
}

C 言論的陰影裏妄想初萌

https://www.cometoj.com/contest/37/problem/C?problem_id=1530

  • 枚舉有多少個點即可。
//? ayaponzu*
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
using namespace std;
typedef long long ll;
typedef double f2;
#define N 100050
#define mod 998244353
int n;
ll P,fac[N],inv[N];
ll qp(ll x,ll y=mod-2) {
    ll re=1; for(;y;y>>=1,x=x*x%mod) if(y&1) re=re*x%mod; return re;
}
ll C(int x,int y) {
    return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int main() {
    int i;
    ll x,y;
    scanf("%d%lld%lld",&n,&x,&y);
    P=x*qp(y)%mod;
    for(fac[0]=i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
    inv[n]=qp(fac[n]);
    for(i=n-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;
    ll ans=0;
    for(i=0;i<=n;i++) {
        ans=(ans+C(n,i)*qp(P,ll(i)*(i-1)/2))%mod;
    }
    printf("%lld\n",ans);
}

D 錯綜的光影所迷惑的思念是

https://www.cometoj.com/contest/37/problem/D?problem_id=1531

  • 不難想到先枚舉點集直徑的中點\(x\),那麽限制就是距離\(x\)\(\frac{i}{2}\)的點在不同的子樹中至少要選出兩個,其他小於\(i\)的可以隨意選。
  • 是一個類似\(2^{sz(x)}-1-\sum\limits_{t}(2^{sz(t)}-1)\)的一個式子。其中\(sz(t)\)表示以\(x\)為根,\(t\)的子樹內有多少距離\(x\)\(i\)的點。
  • 有兩個問題,一是點集直徑中點可能卡在邊上,二是\(i\)可能是奇數,我們在每條邊上塞一個點就行了。
//? ayaponzu*
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <queue>
using namespace std;
typedef long long ll;
#define mod 998244353
#define N 4050
int n,head[N],to[N<<1],nxt[N<<1],cnt;
ll mi[N],ans[N];
int siz[N],ssiz[N][N];
inline void add(int u,int v) {
    to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
}
void dfs(int x,int y,int d,int t) {
    int i; 
    if(x<=n) {
        siz[d]++; ssiz[t][d]++;
    }
    for(i=head[x];i;i=nxt[i]) if(to[i]!=y) {
        dfs(to[i],x,d+1,t);
    }
}
int main() {
    scanf("%d",&n);
    int i,x,y,j;
    for(i=1;i<n;i++) scanf("%d%d",&x,&y),add(x,n+i),add(n+i,x),add(y,n+i),add(n+i,y);
    int ln=n+n-1;
    for(mi[0]=i=1;i<=ln;i++) mi[i]=(mi[i-1]<<1)%mod;
    for(x=1;x<=ln;x++) {
        int s=0,lst=0;
        memset(siz,0,sizeof(siz));
        if(x<=n) {
            siz[0]++; lst++;
        }
        for(i=head[x];i;i=nxt[i]) {
            s++;
            memset(ssiz[s],0,sizeof(ssiz[s]));
            dfs(to[i],x,1,s);
        }
        for(i=1;i<n;i++) {
            ll tmp=mi[siz[i]]-1;
            for(j=1;j<=s;j++) tmp=(tmp-(mi[ssiz[j][i]]-1))%mod;
            ans[i]=(ans[i]+tmp*mi[lst])%mod;
            lst+=siz[i];
        }
    }
    for(i=1;i<n;i++) printf("%lld\n",(ans[i]+mod)%mod);
}

E 情報強者追逐事件

https://www.cometoj.com/contest/37/problem/E?problem_id=1532

orz zsy

  • 對於不在環上的那些,轉移是可以直接合並的,按照拓撲序更新即可,轉移類似\(f_x=f_x+(1-f_x)\times f_y\times s_y\)
  • 剩下的對於每個環來考慮,對於\(x\),會被環上的任意一點\(y\)來更新到,如果在某一時刻環上相鄰兩個點\(u\)\(v\),且從\(u\)轉移出來的概率是\(p\),那麽從\(v\)轉移出去的概率就是\((1-f_v)\times s_u\times p+f_v\),不難發現這是一個\(ax+b\)的形式,放到線段樹上去維護就好了。
//? ayaponzu*
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <queue>
using namespace std;
typedef long long ll;
#define mod 998244353
#define N 800050
char buf[100000],*p1,*p2;
int to[N],du[N],Q[N],n;
ll P[N],s[N],f[N];
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int rd() {
    int x=0; char s=nc();
    while(s<'0'||s>'9') s=nc();
    while(s>='0'&&s<='9') x=(((x<<2)+x)<<1)+s-'0',s=nc();
    return x;
}
ll qp(ll x,ll y=mod-2) {
    ll re=1;for(;y;y>>=1,x=x*x%mod)if(y&1)re=re*x%mod; return re;
}
struct A {
    ll x,y;
    A() {x=y=0;}
    A(ll x_,ll y_) {x=x_,y=y_;}
    A operator + (const A &u) const {
        return A(x*u.x%mod, (y*u.x+u.y)%mod);
    }
}tr[N<<2];
#define ls p<<1
#define rs p<<1|1
int S[N],tp;
void build(int l,int r,int p) {
    if(l==r) {
        int x=l%tp;
        if(!x)x=tp;
        x=S[x];
        int y=(l-1)%tp;
        if(!y)y=tp;
        y=S[y];
        tr[p]=A((1-f[x])*s[y]%mod,f[x]);
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,ls); build(mid+1,r,rs);
    tr[p]=tr[ls]+tr[rs];
}
A query(int l,int r,int x,int y,int p) {
    if(x<=l&&y>=r) return tr[p];
    int mid=(l+r)>>1;
    if(y<=mid) return query(l,mid,x,y,ls);
    else if(x>mid) return query(mid+1,r,x,y,rs);
    else return query(l,mid,x,y,ls)+query(mid+1,r,x,y,rs);
}
void dfs(int x) {
    if(!x||!du[x]) return ;
    S[++tp]=x;
    du[x]=0;
    dfs(to[x]);
}
int main() {
    n=rd();
    int i;
    ll x,y;
    for(i=1;i<=n;i++) {
        x=rd(),y=rd();
        P[i]=x*qp(y)%mod;
    }
    for(i=1;i<=n;i++) to[i]=rd();
    for(i=1;i<=n;i++) du[to[i]]++;
    for(i=1;i<=n;i++) f[i]=P[i];
    for(i=1;i<=n;i++) {
        x=rd(),y=rd();
        s[i]=x*qp(y)%mod;
    }
    int l=0,r=0;
    for(i=1;i<=n;i++) if(!du[i]) {
        Q[r++]=i;
    }
    while(l<r) {
        int x=Q[l++];
        du[to[x]]--;
        f[to[x]] = (f[to[x]]+(1-f[to[x]])*f[x]%mod*s[x])%mod;
        if(!du[to[x]]) {
            Q[r++]=to[x];
        }
    }
    //printf("%d\n",r);
    //for(i=0;i<n;i++) printf("%d\n",Q[i]);
    int j;
    for(i=1;i<=n;i++) {
        if(du[i]) {
            //puts("FUCK");
            tp=0;
            dfs(i);
            //puts("FUCK");
            build(1,tp<<1,1);
            //puts("FUCK");
            for(j=1;j<=tp;j++) f[S[j]]=query(1,tp<<1,j+1,tp+j,1).y;
            //printf("%d\n",tp);
        }
        f[i]=(f[i]%mod+mod)%mod;
        printf("%lld",f[i]);
        if(i!=n) printf(" ");
    }
}

F 真實無妄她們的人生之路

比賽時:

woc這個F我會啊,趕緊寫趕緊寫

woc我怎麽寫了兩個小時還沒過啊

woc終於過了,但是我沒小裙子了...

https://www.cometoj.com/contest/37/problem/E?problem_id=1532

  • \(w_i=\frac{P_i}{1-P_i}\),表示選\(i\)產生的概率,最後答案再乘上\(\prod\limits_{i}(1-P_i)\)就可以了。
  • \(f_j=[x^j](\prod\limits_{i=1}^{n}(w_ix+1))\),也就是選\(j\)個的概率,這個可以分治\(ntt\)求出。
  • 那麽考慮刪掉一個,假設刪掉第\(i\)個,設退掉\(i\)之後的背包數組為\(g_j\)就是強制\(i\)不選,選\(j\)個的概率。
  • 根據退背包那一套理論,可以得出\(g_j=f_j-g_{j-1}\times w_i\)
  • 展開,可得\(g_j=\sum\limits_{k=0}^j(-1)^kw_i^kf_{j-k}\)
  • 那麽退掉第\(i\)個的答案\(ans_i=\sum\limits_{j=1}^na_j\sum\limits_{k=0}^j(-1)^kw_i^kf_{j-k}\)
  • \(ans_i=\sum\limits_{k=0}^n(-1)^kw_i^k\sum\limits_{j=k}^na_jf_{j-k}\)
  • \(h_k=\sum\limits_{j=k}^na_jf_{j-k}\),這是一個卷積形式。
  • 那麽\(ans_i=\sum\limits_{k=0}^n(-1)^kw_i^kh_k\), 多點求值即可。

技術分享圖片

  • 把概率為\(1\)的扔掉再把\(a\)數組平移若幹位來求其他位置上的答案然後由於那些被扔掉的位置答案都相同所以用一開始的\(f\)和平移(若幹位\(-1\))後的\(a\)數組求點積即可。
  • 或者把上面的\(w_i\)改成\(\frac{1-P_i}{P_i}\),orzzsy。
//? ayaponzu*
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <queue>
using namespace std;
typedef long long ll;
#define mod 998244353
#define N 800050
char buf[100000],*p1,*p2;
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int rd() {
    int x=0; char s=nc();
    while(s<'0'||s>'9') s=nc();
    while(s>='0'&&s<='9') x=(((x<<2)+x)<<1)+s-'0',s=nc();
    return x;
}
ll mem[N*60],*ptr=mem,fac[N],inv[N];
ll qp(ll x,ll y) {ll re=1;for(;y;y>>=1,x=x*x%mod)if(y&1)re=re*x%mod;return re;}
ll INV(ll x){return qp(x,mod-2);}
void ntt(ll *a,int len,int flg) {
    int i,j,k,t; ll tmp,w,wn;
    for(i=k=0;i<len;i++) {
        if(i>k) swap(a[i],a[k]);
        for(j=len>>1;(k^=j)<j;j>>=1) ;
    }
    for(k=2;k<=len;k<<=1) {
        t=k>>1; wn=qp(3,(mod-1)/k);
        if(flg==-1) wn=INV(wn);
        for(i=0;i<len;i+=k) {
            w=1;
            for(j=i;j<i+t;j++) {
                tmp=a[j+t]*w%mod;
                a[j+t]=(a[j]-tmp)%mod;
                a[j]=(a[j]+tmp)%mod;
                w=w*wn%mod;
            }
        }
    }if(flg==-1)for(tmp=INV(len),i=0;i<len;i++) a[i]=a[i]*tmp%mod;
}
ll A[N],B[N];
struct shion {
    ll *a;
    int len;
    shion() {}
    shion(int l) {len=l,a=ptr,ptr+=l;}
    void fix(int l) {len=l,a=ptr,ptr+=l;}
    void rev() {reverse(a,a+len);}
    void get_mod(int l) {int i;for(i=l;i<len;i++)a[i]=0;len=l;}
    void kuo(int l) {len=l,ptr=mem+len;}
    bool operator < (const shion &u) const {
        return len>u.len;
    }
    shion operator - (const shion &u) const {
        shion re(len);int i;
        for(i=0;i<len;i++) re.a[i]=(a[i]-u.a[i])%mod;
        return re;
    }
    shion operator + (const shion &u) const {
        shion re(max(len,u.len)); int i;
        for(i=0;i<re.len;i++) {
            re.a[i]=((i<len?a[i]:0)+(i<u.len?u.a[i]:0))%mod;
        }return re;
    }
    shion operator * (const shion &u) const {
        shion re(len+u.len-1);
        int i,l=1,j;
        if(re.len<=200) {
            for(i=0;i<len;i++)for(j=0;j<u.len;j++) {
                re.a[i+j]=(re.a[i+j]+ll(a[i])*u.a[j])%mod;
            }return re;
        }
        while(l<len+u.len)l<<=1;
        memset(A,0,sizeof(ll)*l);
        memset(B,0,sizeof(ll)*l);
        memcpy(A,a,sizeof(ll)*len);
        memcpy(B,u.a,sizeof(ll)*u.len);
        ntt(A,l,1),ntt(B,l,1);
        for(i=0;i<l;i++) A[i]=ll(A[i])*B[i]%mod;
        ntt(A,l,-1);
        for(i=0;i<re.len;i++) re.a[i]=A[i];
        return re;
    }
    void get_inv(const shion &u,int len) {
        if(len==1) {a[0]=INV(u.a[0]);return ;}
        get_inv(u,len>>1);
        int l=len<<1,i;
        memset(A,0,sizeof(ll)*l);
        memset(B,0,sizeof(ll)*l);
        memcpy(A,u.a,sizeof(ll)*min(u.len,len));
        memcpy(B,a,sizeof(ll)*len);
        ntt(A,l,1),ntt(B,l,1);
        for(i=0;i<l;i++) A[i]=B[i]*(2-ll(A[i])*B[i]%mod)%mod;
        ntt(A,l,-1);
        memcpy(a,A,sizeof(ll)*len);
    }
    shion Get_inv(int l) {
        shion re(l); re.get_inv(*this,l); return re;
    }
    shion operator / (shion u) {
        int n=len,m=u.len,l=1;
        while(l<(n-m+1))l<<=1;
        rev(),u.rev();
        shion v=u.Get_inv(l);
        v.get_mod(n-m+1);
        shion re=*this*v;
        rev(),u.rev();
        re.get_mod(n-m+1);
        re.rev();
        return re;
    }
    shion operator % (shion u) {
        shion re=*this-u*(*this/u); re.get_mod(u.len-1); return re;
    }
    shion Get_dao() {
        shion re(len-1);
        int i;
        for(i=1;i<len;i++) re.a[i-1]=ll(i)*a[i]%mod;
        return re;
    }
}fq[N<<2];
#define ls p<<1
#define rs p<<1|1
ll RE[N],qx[N];
void build(int l,int r,int p) {
    if(l==r) {
        fq[p].fix(2);
        fq[p].a[0]=-qx[l];
        fq[p].a[1]=1;
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,ls),build(mid+1,r,rs);
    fq[p]=fq[ls]*fq[rs];
}
void get_val(shion F,int l,int r,int p,ll *re) {
    if(F.len<=450) {
        int i,j;
        ll t;
        for(i=l;i<=r;i++) {
            for(j=0,t=1;j<F.len;j++,t=ll(t)*qx[i]%mod) {
                re[i]=(re[i]+ll(t)*F.a[j])%mod;
            }
        }
        return ;
    }
    int mid=(l+r)>>1;
    get_val(F%fq[ls],l,mid,ls,re);
    get_val(F%fq[rs],mid+1,r,rs,re);
}
priority_queue<shion>q;
int n;
ll P[N],a[N],f[N],g[N],num[N],h[N],tmpa[N];
void geth() {
    shion ta(n+1),tb(n+1);
    int i;
    for(i=0;i<=n;i++) ta.a[i]=a[n-i];
    for(i=0;i<=n;i++) tb.a[i]=f[i];
    shion tc=ta*tb;
    for(i=0;i<=n;i++) h[i]=tc.a[n-i];
}
int is[N],to[N];
int main() {

    n=rd();

    int i,tot=0,ln=n;
    ll x,y;
    for(i=0;i<n;i++) a[i]=rd();
    for(i=1;i<=ln;i++) {
        x=rd(),y=rd();
        P[i]=x*INV(y)%mod;
        num[i]=INV(1-P[i]);
        if(P[i]==1) {
            is[i]=1;
        }else {
            P[++tot]=P[i];
            num[tot]=num[i];
            to[i]=tot;
        }
    }
    if(!tot) {
        for(i=1;i<=n;i++) {
            printf("%lld",a[ln-1]);
            if(i!=n) printf(" ");
        }
        return 0;
    }
    memcpy(tmpa,a,sizeof(a));
    for(i=0;i<=n;i++) a[i]=a[i+ln-tot];
    n=tot;
    for(i=1;i<=n;i++) {
        shion t(2);
        t.a[0]=1,t.a[1]=P[i]*num[i]%mod;
        q.push(t);
    }
    for(i=1;i<n;i++) {
        shion aa=q.top(); q.pop();
        shion bb=q.top(); q.pop();
        q.push(aa*bb);
    }

    ll tmp=1;
    for(i=1;i<=n;i++) tmp=tmp*(1-P[i])%mod;
    shion qwq=q.top();
    for(i=0;i<=n;i++) f[i]=qwq.a[i];
    geth();
    
    
    for(i=1;i<=n;i++) qx[i]=P[i]*num[i]%mod;
    build(1,n,1);
    shion ff(n+1);
    for(i=0;i<=n;i++) {
        if(i&1) ff.a[i]=mod-h[i];
        else ff.a[i]=h[i];
    }
    get_val(ff,1,n,1,RE);

    if(tot!=ln) for(i=0;i<=n;i++) a[i]=tmpa[i+ln-tot-1];
    ll OTH=0;
    for(i=0;i<=n;i++) {
        //printf("%lld\n",a[i]);
        OTH=(OTH+a[i]*f[i])%mod;
    }
    OTH=(OTH*tmp%mod+mod)%mod;

    for(i=1;i<=ln;i++) {
        if(is[i]) {
            printf("%lld",OTH);
        }else {
            ll ans=RE[to[i]]*num[to[i]]%mod*tmp%mod;
            printf("%lld",(ans+mod)%mod);
        }
        if(i!=ln) printf(" ");
    }
}

技術分享圖片

Comet OJ - Contest #2簡要題解