1. 程式人生 > >[NOIP2018模擬賽10.18]自閉報告

[NOIP2018模擬賽10.18]自閉報告

閒扯

這一天,菜雞RyeCatcher又想起來了被毒瘤題支配的恐懼

今天比較好玩,還是ljy提醒才發現資料夾裡有題面...不知道外面的人什麼時候才發現

看完了題面,又回到了雅禮啥題也不會寫的感覺

T1 發現操作就是交換兩個數於是寫了個假做法就是不同的數之和;分類討論後文件夾裡突然出現一個大樣例!發現我的輸出居然少5!?於是又分類討論碼碼碼.後面又有人說大樣例是假的woc...T2 暴力 沒碼完 T3 沒思路...

結果30+0+0涼涼,分類討論多給了我10分hhh

下午講題的時候因為1926過於矚目被欽點了,因為碼風一貫過長被出題人問是不是正解寫掛了也是尷尬...

啊滑鼠電池沒電了好煩啊 QAQ

T1 duliu

操作就是交換你手中的數和數列中的一個數

怎麼判-1?把異或和放在末尾判斷排序後是否完全相等(雖然用雜湊表也可以)

然後把數字離散化之後將\(a[i],b[i](a[i]!=b[i])\)連邊,我們發現如果你手中有一個聯通塊中的數,那麼這個聯通塊中的數你都可以經過交換得到,但是考慮你從一個聯通塊跳到另一個還需要換一次數.所以答案為聯通塊個數+各個聯通塊的大小.

這個聯通塊的大小怎麼定義?對於聯通塊點集\(T\), \(size[T] =\sum_{x \in T} times[x]\),\(times[x]\)\(x\)\(a\)陣列中的出現次數

但是還有一件事要注意,就是特判你當前手中的數--\(a\)

陣列的異或之和

/*
  code by RyeCatcher
*/
inline char gc(){
    static char buf[SIZE],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,SIZE,stdin),p1==p2)?EOF:*p1++;
}
#define gc getchar
template <class T>inline void read(T &x){
    x=0;int ne=0;char c;
    while((c=gc())>'9'||c<'0')ne=c=='-';x=c-48;
    while((c=gc())>='0'&&c<='9')x=(x<<3)+(x<<1)+c-48;x=ne?-x:x;return ;
}
gp_hash_table <int,int> g;
const int maxn=100005;
const int inf=0x7fffffff;
int f[maxn],tot=0;
int a[maxn],b[maxn],c[maxn],d[maxn],n;
int fa[maxn<<1],size[maxn<<1];
bool vis[maxn<<1];
int get(int x){return (fa[x]==x)?fa[x]:(fa[x]=get(fa[x]));}
inline void merge(int x,int y){
    x=get(x),y=get(y);
    if(x==y){
        //size[x]++;
        return ;
    }
    if(size[x]<size[y]){
        size[y]+=size[x];
        fa[x]=y;
    }
    else{
        size[x]+=size[y];
        fa[y]=x;
    }
    return ;
}
ll ans=0;
int main(){
    //DEBUG
    //freopen("dat.in","r",stdin);
    //freopen("duliu_3.in","r",stdin);
    int x,pre_sum=0;
    read(n);
    for(ri i=1;i<=n;i++){
        read(x);
        pre_sum^=x;
        if(!g[x]){
            g[x]=++tot;
            vis[tot]=0,size[tot]=0,fa[tot]=tot;
            f[tot]=x;
        }
        c[i]=a[i]=g[x];
    }
    if(!g[pre_sum]){
        g[pre_sum]=++tot,f[tot]=g[
pre_sum];
        vis[tot]=0,size[tot]=0,fa[tot]=tot;
    }
    //printf("--%d--\n",pre_sum);
    a[n+1]=c[n+1]=g[pre_sum];
    pre_sum=0;
    for(ri i=1;i<=n;i++){
        read(x);
        pre_sum^=x;
        if(!g[x]){
            g[x]=++tot;
            vis[tot]=0,size[tot]=0,fa[tot]=tot;
            f[tot]=x;
        }
        d[i]=b[i]=g[x];
        if(a[i]!=b[i])size[a[i]]++;
    }
    if(!g[pre_sum]){
        g[pre_sum]=++tot,f[tot]=g[pre_sum];
        vis[tot]=0,size[tot]=1,fa[tot]=tot;
    }
    b[n+1]=d[n+1]=g[pre_sum];
    n++;    
    std::sort(c+1,c+1+n);std::sort(d+1,d+1+n);
    for(ri i=1;i<=n;i++){
        if(c[i]!=d[i]){puts("-1");exit(0);}
        if(a[i]!=b[i]){
            merge(a[i],b[i]);
        }
    }
    int cnt=0;
    x=get(a[n]);
    if(size[x]==0){//用if(a[n]==b[n])更好
        ans=0,cnt=1;
    }
    for(ri i=1;i<n;i++){
        x=get(a[i]);
        if(!vis[x]){
            if(size[x]==0)continue;
            cnt++,vis[x]=1;
            ans+=size[x];
        }
    }
    printf("%lld\n",ans+cnt-1);
    return 0;
}

T2 travel

又是道樹形DP神仙題

這個平方和期望期望有點毒,類比搞矩陣時的非齊次線性遞推(似乎叫這個名字)

\((a+b)^2 = a^2 + 2 \times ab +b^2\)

又轉化成線性的了

先想想怎麼算\(F\)值,倍增/鏈剖都是資瓷的.然而題解有一種高明的線性做法---樹上差分+棧

我們dfs到一個點\(x\)將該點入棧,從這點回溯到父親就出棧,發現\(y=st[max(0,top-d[x]-1)]\)就是能走到最遠的點的父親

然後\(tag[x]+=a[x],tag[y]-=a[x]\) ,最後按照樹上差分套路求波子樹和就好了

求期望考慮naiive 的樹形DP,欽定每一個點為根計算答案

\(g[x]\)表示\(x\)子樹聯通塊和平方期望,按上面式子推;\(s[x]\)表示\(x\)子樹期望和,這是可以線性推的

一開始\(g[x]=F[x]^2,s[x]=F[x]\)

\(g[x] = p \times (g[x]+2 \times s[x] \times s[son[x]]+g[son[x]]) +(1-p) \times g[x]\)

\(s[x]= p \times (s[x]+s[son[x]])+(1-p) \times s[x]\)

對於\(x\)的每個兒子這麼合併就好了

這樣是\(O(nq)\)\(O(n^2)\)的,發現這個可以二次掃描加換根搞

如果不知道建議先去學一學https://rye-catcher.github.io/tags/%E4%BA%8C%E6%AC%A1%E6%89%AB%E6%8F%8F%E4%B8%8E%E6%8D%A2%E6%A0%B9/

二次換根是從上往下遞推的,我們對於點\(x\),求出\(o[x][0/1]\),表示向上的聯通塊和平方期望&和期望,再對於\(x\)的兒子的遍歷順序搞一個字首和字尾記錄兄弟子樹的聯通塊平方和期望與和期望,然後逐個合併就好了

這個還是要考慮蠻多細節...現在還沒調出來

/*
  code by RyeCatcher
*/
inline char gc(){
    static char buf[SIZE],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,SIZE,stdin),p1==p2)?EOF:*p1++;
}
#define gc getchar
template <class T>inline void read(T &x){
    x=0;int ne=0;char c;
    while((c=gc())>'9'||c<'0')ne=c=='-';x=c-48;
    while((c=gc())>='0'&&c<='9')x=(x<<3)+(x<<1)+c-48;x=ne?-x:x;return ;
}
const int maxn=200005;
const int inf=0x7fffffff;
const ll P=998244353;
int n,q;
struct Edge{
    int ne,to;
    ll p;
}edge[maxn<<1];
int h[maxn],num_edge=1;
inline void add_edge(int f,int to,ll c){
    edge[++num_edge].ne=h[f];
    edge[num_edge].to=to;
    edge[num_edge].p=c;
    h[f]=num_edge;
}
int d[maxn];
ll a[maxn],tag[maxn],f[maxn],g[maxn],s[maxn];
int st[maxn],top=0,fa[maxn];
void pre_dfs(int now){
    int v,x;
    x=st[max(0,top-d[now])];
    st[++top]=now,tag[now]=(tag[now]+a[now])%P,tag[x]=(tag[x]+P-a[now])%P;
    for(ri i=h[now];i;i=edge[i].ne){
        v=edge[i].to;
        if(v==fa[now])continue;
        fa[v]=now;
        pre_dfs(v);
    }
    top--;
    return ;
}
void get_sum(int now){
    int v;
    f[now]=(f[now]+tag[now]+P)%P;
    for(ri i=h[now];i;i=edge[i].ne){
        v=edge[i].to;
        if(v==fa[now])continue;
        get_sum(v);
        f[now]=(f[now]+f[v])%P;
    }
    return ;
}
int rt;
ll fa_dis[maxn],ans[maxn],pp;
struct Dat{
    int s;
    ll p;
    Dat(int _s,ll _p){s=_s,p=_p;}
};
vector <Dat> son[maxn];
void dfs_1(int now,int fa){
    int v;
    g[now]=f[now]*f[now]%P,s[now]=f[now];
    for(ri i=h[now];i;i=edge[i].ne){
        v=edge[i].to;
        if(v==fa)continue;  
        dfs_1(v,now);
        pp=edge[i].p;
        fa_dis[v]=pp;
        son[now].push_back(Dat(v,pp));
        g[now]=(g[now]+pp*((2*s[now]*s[v]%P+g[v])%P)%P)%P;
        s[now]=((s[now]+s[v])*pp%P+((1-pp)%P+P)*s[now]%P)%P;
    }
    return ;
}
ll pre[maxn][2],suf[maxn][2],o[maxn][2];//兄弟字首  兄弟字尾 上方 
void dfs_2(int now,int fa){
    int x,v;
    unsigned int size=son[now].size();
    printf("--%d--\n",now);
    ans[now]=((g[now]+o[now][0])%P+2*s[now]%P*o[now][1]%P)%P;
    pre[now][0]=o[now][0],pre[now][1]=o[now][1];
    suf[now][0]=suf[now][1]=0;
    
    for(ri i=0;i<size;i++){
        v=son[now][i].s,pp=son[now][i].p;
        //printf("%d ",v);
        o[v][0]=(o[v][0]+pp*(2*o[v][1]%P*pre[now][1]%P+pre[now][0])%P)%P;
        o[v][1]=((o[v][1]+pre[now][1])%P*pp%P+((1-pp)%P+P)*o[v][1]%P)%P;
        printf("**%d %d %lld %lld\n",now,v,pre[now][0],pre[now][1]);
        pre[now][0]=(pre[now][0]+pp*(2*pre[now][1]%P*s[v]%P+g[v])%P)%P;
        pre[now][1]=((pre[now][1]+s[v])%P*pp%P+((1-pp)%P+P)*pre[now][1]%P)%P;
        
        v=son[now][size-i-1].s,pp=son[now][size-i-1].p;
        //printf("--%d-- \n",v);
        if(v==3){
            printf("%lld %lld %lld\n",o[v][0],suf[now][0],suf[now][1]);
        }
        o[v][0]=(o[v][0]+pp*(2*o[v][1]%P*suf[now][1]%P+suf[now][0])%P)%P;
        o[v][1]=((o[v][1]+suf[now][1])%P*pp%P+((1-pp)%P+P)*o[v][1]%P)%P;
        suf[now][0]=(suf[now][0]+pp*(2*suf[now][1]%P*s[v]%P+g[v])%P)%P;
        suf[now][1]=((suf[now][1]+s[v])%P*pp%P+((1-pp)%P+P)*suf[now][1]%P)%P;
    }
    for(ri i=0;i<size;i++){
        v=son[now][i].s;
        o[v][0]=(o[v][0]+2*o[v][1]*f[v]%P+f[v]*f[v]%P)%P;
        o[v][1]=(o[v][1]+f[v])%P;
        dfs_2(v,now);
    }
    return ;
}
int main(){
    //freopen("travel_1.in","r",stdin);
    //freopen("wa.out","w",stdout);
    int x,y;ll z;
    read(n);
    for(ri i=1;i<=n;i++){
        read(a[i]),read(d[i]);
    }
    for(ri i=1;i<n;i++){
        read(x),read(y),read(z);
        add_edge(x,y,z);
        add_edge(y,x,z);
    }
    fa[1]=0;
    pre_dfs(1);
    get_sum(1);
    dfs_1(1,0);
    dfs_2(1,0);
    read(q);
    while(q--){
        read(x);
        printf("%lld\n",ans[x]%P);
    }
    return 0;
}

T3

咕咕咕