1. 程式人生 > >牛客練習賽26遊記

牛客練習賽26遊記

auto 就是 表示 記錄 const 編號 mes def fir

牛客練習賽26遊記

A-平面

題目大意:

在平面上畫\(n(n\le10^9)\)個X,最多將平面分成幾部分?

思路:

如果是畫直線的話答案是\(\frac{n^2+n+2}2\)

畫X相當於畫兩條直線,因此答案是\(2n^2+n+1\)

時間復雜度\(\mathcal O(1)\)

源代碼:

#include<cstdio>
#include<cctype>
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
typedef long long int64;
int main() {
    const int n=getint()*2;
    printf("%lld\n",((int64)n*n+n+2)/2);
    return 0;
}

B-煙花

題目大意:

\(n(n\le10^5)\)個煙花,每個煙花代表著互不相同的顏色,對於第\(i\)個煙花,它有\(p_i\)的概率被成功點燃。求產生顏色的期望個數及產生恰好\(k(k\le200)\)種顏色的概率。

思路:

顯然期望個數就是\(\sum p_i\)

而產生恰好\(k\)種顏色的概率可以DP。

\(f_{i,j}\)表示前\(i\)個煙花中成功點燃\(j\)個的概率,轉移方程為\(f_{i,j}=f_{i-1,j-1}\times p_i+f_{i-1,j}\times(1-p_i)\)

時間復雜度\(\mathcal O(nk)\)

源代碼:

#include<cstdio>
#include<cctype>
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
const int N=1e5+1,M=201;
double p[N],f[N][M];
int main() {
    const int n=getint(),m=getint();
    for(register int i=1;i<=n;i++) {
        scanf("%lf",&p[i]);
    }
    f[0][0]=1;
    for(register int i=1;i<=n;i++) {
        f[i][0]=f[i-1][0]*(1-p[i]);
        for(register int j=1;j<=m;j++) {
            f[i][j]=f[i-1][j]*(1-p[i])+f[i-1][j-1]*p[i];
        }
    }
    double ans=0;
    for(register int i=1;i<=n;i++) ans+=p[i];
    printf("%.4f\n%.4f\n",ans,f[n][m]);
    return 0;
}

C-城市規劃

題目大意:

有一條\(n(n\le10^6)\)個點的鏈,各結點編號依次為\(1\sim n\)。另有\(m(m\le10^7)\)個限制\((x_i,y_i)\),要求你從鏈上去掉一些邊,使得對於所有的限制,\(x_i\)\(y_i\)均不連通。求最少刪掉幾條邊。

思路:

如果我們假設結點\(1,2\)之間的邊編號為\(2\)\(2,3\)之間的邊編號為\(3\)。將約束條件從點轉化成邊,則\([x_i+1,y_i]\)之間一定有邊要被刪掉。

方便起見,下面我們用\(x_i\)表示前面的\(x_i+1\)

對於每個\(y_i\)只保留最大的\(x_i\)

從左往右掃一遍,記錄刪去的最右的邊\(p\)

。如果當前點是某個\(y_i\),且對應的\(x_i\le p\),說明\(x_i\sim y_i\)之間已經不連通了,這時不需要進行任何操作。如果\(x_i>p\),則將\(x_i\)作為新的\(p\)ans++即可。

時間復雜度\(\mathcal O(n+m)\),但是比較卡常。

源代碼:

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<sys/mman.h>
#include<sys/stat.h>
class MMapInput {
    private:
        char *buf,*p;
        int size;
    public:
        MMapInput() {
            register int fd=fileno(stdin);
            struct stat sb;
            fstat(fd,&sb);
            size=sb.st_size;
            buf=reinterpret_cast<char*>(mmap(0,size,PROT_READ,MAP_PRIVATE,fileno(stdin),0));
            p=buf;
        }
        char getchar() {
            return (p==buf+size||*p==EOF)?EOF:*p++;
        }
};
MMapInput mmi;
inline int getint() {
    register char ch;
    while(!isdigit(ch=mmi.getchar()));
    register int x=ch^'0';
    while(isdigit(ch=mmi.getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
const int N=1e6+1;
int cnt[N],left[N];
int main() {
    const int n=getint(),m=getint();
    for(register int i=0;i<m;i++) {
        const int b=getint()+1,e=getint();
        left[e]=std::max(left[e],b);
    }
    int ans=0;
    for(register int i=1,p=0;i<=n;i++) {
        if(p<left[i]) {
            ans++;
            p=i;
        }
    }
    printf("%d\n",ans);
    return 0;
}

D-xor序列

題目大意:

一個長度為\(n(n\le10^5)\)的數列,\(q(q\le10^5)\)次詢問,每次詢問\(x\)能否通過與數列中的一些數進行異或運算使得最終結果為\(y\)

思路:

線性基。

源代碼:

#include<cstdio>
#include<cctype>
#include<algorithm>
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
const int N=32;
int b[N];
int main() {
    const int n=getint();
    for(register int i=0;i<n;i++) {
        int x=getint();
        for(register int i=N-1;~i;i--) {
            if(!(x&(1ll<<i))) continue;
            if(!b[i]) {
                b[i]=x;
                break;
            } else {
                x^=b[i];
            }
        }
    }
    const int m=getint();
    for(register int i=0;i<m;i++) {
        int x=getint()^getint();
        for(register int i=N-1;~i;i--) {
            if(x>>i&1) x^=b[i];
        }
        puts(x==0?"YES":"NO");
    }
    return 0;
}

E-樹上路徑

題目大意:

給出一個\(n\)個點的樹,\(1\)號節點為根節點,每個點有一個權值。\(m(m\le10^5)\)次操作,操作包含以下\(3\)種:

  1. 將以\(x\)為根的子樹內節點的權值加\(val\)
  2. \((x,y)\)路徑上的節點權值加\(val\)
  3. 詢問\((x,y)\)路徑上節點的權值兩兩相乘的和。

思路:

首先考慮數列上的情況,對於區間\([l,r]\)。若我們記\(a\)\([l,r]\)的和,\(b\)\([l,r]\)的平方和,則答案為\(\frac{a^2-b}2\)

可以使用線段樹來維護,利用樹鏈剖分將這種做法推廣到樹上。

時間復雜度\(\mathcal O(m\log^2 n)\)

源代碼:

#include<cstdio>
#include<cctype>
#include<vector>
inline int getint() {
    register char ch;
    while(!isdigit(ch=getchar()));
    register int x=ch^'0';
    while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    return x;
}
using int64=long long;
const int N=1e5+1,mod=1e9+7,inv=5e8+4;
int w[N],par[N],top[N],dep[N],size[N],son[N],dfn[N],id[N];
std::vector<int> e[N];
inline void add_edge(const int &u,const int &v) {
    e[u].emplace_back(v);
    e[v].emplace_back(u);
}
void dfs(const int &x,const int &par) {
    size[x]=1;
    ::par[x]=par;
    dep[x]=dep[par]+1;
    for(auto &y:e[x]) {
        if(y==par) continue;
        dfs(y,x);
        size[x]+=size[y];
        if(size[y]>size[son[x]]) {
            son[x]=y;
        }
    }
}
void dfs(const int &x) {
    dfn[x]=++dfn[0];
    id[dfn[x]]=x;
    top[x]=x==son[par[x]]?top[par[x]]:x;
    if(son[x]) dfs(son[x]);
    for(auto &y:e[x]) {
        if(y==par[x]||y==son[x]) continue;
        dfs(y);
    }
}
class SegmentTree {
    #define _left <<1
    #define _right <<1|1
    #define mid ((b+e)>>1)
    private:
        int tag[N<<2],sum[N<<2],ssum[N<<2];
        void update(const int &p,const int &b,const int &e,const int &x) {
            (tag[p]+=x)%=mod;
            ssum[p]=((ssum[p]+(int64)x*x%mod*len(b,e)%mod)%mod+(int64)2*sum[p]%mod*x%mod)%mod;
            sum[p]=(sum[p]+(int64)x*len(b,e))%mod;
        }
        void push_up(const int &p) {
            sum[p]=(sum[p _left]+sum[p _right])%mod;
            ssum[p]=(ssum[p _left]+ssum[p _right])%mod;
        }
        void push_down(const int &p,const int &b,const int &e) {
            if(!tag[p]) return;
            update(p _left,b,mid,tag[p]);
            update(p _right,mid+1,e,tag[p]);
            tag[p]=0;
        }
        inline int len(const int &b,const int &e) const {
            return e-b+1;
        }
    public:
        void build(const int &p,const int &b,const int &e) {
            tag[p]=0;
            if(b==e) {
                sum[p]=w[id[b]];
                ssum[p]=(int64)w[id[b]]*w[id[b]]%mod;
                return;
            }
            build(p _left,b,mid);
            build(p _right,mid+1,e);
            push_up(p);
        }
        void modify(const int &p,const int &b,const int &e,const int &l,const int &r,const int &x) {
            if(b==l&&e==r) {
                update(p,b,e,x);
                return;
            }
            push_down(p,b,e);
            if(l<=mid) modify(p _left,b,mid,l,std::min(mid,r),x);
            if(r>mid) modify(p _right,mid+1,e,std::max(mid+1,l),r,x);
            push_up(p);
        }
        std::pair<int,int> query(const int &p,const int &b,const int &e,const int &l,const int &r) {
            if(b==l&&e==r) {
                return std::make_pair(sum[p],ssum[p]);
            }
            int ret0=0,ret1=0;
            push_down(p,b,e);
            if(l<=mid) {
                const auto q=query(p _left,b,mid,l,std::min(mid,r));
                (ret0+=q.first)%=mod;
                (ret1+=q.second)%=mod;
            }
            if(r>mid) {
                const auto q=query(p _right,mid+1,e,std::max(mid+1,l),r);
                (ret0+=q.first)%=mod;
                (ret1+=q.second)%=mod;
            }
            return std::make_pair(ret0,ret1);
        }
    #undef _left
    #undef _right
    #undef mid
};
SegmentTree t;
inline int query(int x,int y) {
    int ret0=0,ret1=0;
    while(top[x]!=top[y]) {
        if(dep[top[x]]<dep[top[y]]) std::swap(x,y);
        const auto q=t.query(1,1,dfn[0],dfn[top[x]],dfn[x]);
        (ret0+=q.first)%=mod;
        (ret1+=q.second)%=mod;
        x=par[top[x]];
    }
    if(dep[x]<dep[y]) std::swap(x,y);
    const auto q=t.query(1,1,dfn[0],dfn[y],dfn[x]);
    (ret0+=q.first)%=mod;
    (ret1+=q.second)%=mod;
    return ((int64)ret0*ret0%mod-ret1+mod)%mod;
}
inline void modify(int x,int y,const int &val) {
    while(top[x]!=top[y]) {
        if(dep[top[x]]<dep[top[y]]) std::swap(x,y);
        t.modify(1,1,dfn[0],dfn[top[x]],dfn[x],val);
        x=par[top[x]];
    }
    if(dep[x]<dep[y]) std::swap(x,y);
    t.modify(1,1,dfn[0],dfn[y],dfn[x],val);
}
int main() {
    const int n=getint(),m=getint();
    for(register int i=1;i<=n;i++) w[i]=getint();
    for(register int i=1;i<n;i++) {
        add_edge(getint(),getint());
    }
    dfs(1,0);
    dfs(1);
    t.build(1,1,n);
    for(register int i=0;i<m;i++) {
        const int opt=getint();
        if(opt==1) {
            const int x=getint(),val=getint();
            t.modify(1,1,n,dfn[x],dfn[x]+size[x]-1,val);
        }
        if(opt==2) {
            const int x=getint(),y=getint(),val=getint();
            modify(x,y,val);
        }
        if(opt==3) {
            const int x=getint(),y=getint();
            printf("%d\n",(int64)query(x,y)*inv%mod);
        }
    }
    return 0;
}

牛客練習賽26遊記