1. 程式人生 > 其它 >10.26模擬賽

10.26模擬賽

模擬賽改錯

預感最近的模擬賽都是CF重組場。

T1

考場思路:

純暴力模擬。

先求出所有點到\(0\)號點的路徑上經過的節點。

然後再轉化為兩個點之間的路徑(0號點到左端點和0號點到左端點減去0號點到lca),記錄哪些點在路徑上,對於每個路徑求出mex。

最後統計mex的貢獻。

瓶頸在於求解mex需要依賴於求出具體每條路徑經過了哪些節點。

時間複雜度應該在\(O(n^2logn)\)左右,應該可以拿30分,但是實際上掛到了5分。

正解思路:

一條路徑的mex為\(k\)的充要條件為:\([0,k-1]\)在這條路徑上都出現了,但是\(k\)沒有出現。

維護\(k\)在一條路徑上沒有出現是不好維護的,考慮容斥出來用\(mex\ge k\)

的路徑條數,減去\(mex\ge k+1\)的路徑條數,即為\(mex\)\(k\)的路徑條數。

現在問題轉化為了求\(mex\)\([0,n-1]\)的路徑條數。

考慮依次列舉每個點。

如果當前列舉的點是\(k\)\([0,k-1]\)都在一條鏈上,鏈上的端點為\(st\)\(ed\)

如果\(k\)也在這條鏈上,那麼它有可能成為鏈的端點。

如果\(st\)\(ed\)的lca不為其中一個,說明此時鏈不被算在st或ed的子樹中(類似一下這種情況)。那麼路徑條數為\(siz[st]\times siz[ed]\)

從st的子樹(不包含當前鏈)中的任一節點到ed的子樹(不包含當前鏈)中的任一節點都經過了當前鏈,這些路徑的mex一定大於等於\(k\)

如果\(st\)\(ed\)的lca為其中一個,說明此時鏈被算在st或ed的其中一個子樹中(類似於以下這種情況)。

此時的路徑條數為\(siz[st]\times(n-siz[ed'])\)

如果\(k\)不在這條鏈上,那麼\([k,n]\)之間的\(mex\)一定為\([0,k-1]\)之間的數,而\(mex\)\([0,k-1]\)的路徑數已經統計過了,此時就可以跳出了。

code
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
int const maxe=400101;
int const maxn=200101;
int const Log=19;
int t,n;
struct Edge{
    int nxt,to;
}e[maxe];
int head[maxn],tot;
int dep[maxn],f[maxn][Log+1];
int siz[maxn];
void addedge(int x,int y){
    e[++tot].nxt=head[x];
    head[x]=tot;
    e[tot].to=y;
}
void dfs(int x,int fa){
    dep[x]=dep[fa]+1;siz[x]=1;
    f[x][0]=fa;
    for(int k=1;k<=Log;k++)
        f[x][k]=f[f[x][k-1]][k-1];
    for(int i=head[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa) continue;
        dfs(y,x);
        siz[x]+=siz[y];
    }
}
int get_lca(int x,int y){
    if(dep[x]>dep[y]) swap(x,y);
    for(int i=Log;i>=0;i--)
        if(dep[f[y][i]]>=dep[x])
            y=f[y][i];
    if(x==y) return x;
    for(int i=Log;i>=0;i--)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];
}
int dist(int x,int y){
    int lca=get_lca(x,y);
    return dep[x]+dep[y]-2*dep[lca];
}
LL ans[maxn];
int main(){
    scanf("%d",&t);
    while(t--){
        memset(head,0,sizeof(head));tot=0;
        memset(ans,0ll,sizeof(ans));
        scanf("%d",&n);
        for(int i=1;i<=n-1;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            addedge(x,y);addedge(y,x);
        }
        dfs(0,0);
        ans[0]=1ll*(n)*(n-1);ans[0]>>=1ll;

        for(int i=head[0];i;i=e[i].nxt){
            int y=e[i].to;
            ans[1]+=1ll*siz[y]*(n-siz[y]);
        }
        ans[1]+=1ll*(siz[0]-1)*(n-siz[0]+1);ans[1]>>=1ll;

        int st=0,ed=0;
        for(int i=1;i<n;i++){
            int len1=dist(st,ed),len2=dist(st,i)+dist(i,ed);
            if(len1!=len2){
                if(len2-len1==2*dist(i,st)) st=i;
                else if(len2-len1==2*dist(i,ed)) ed=i;
                else break;
            }
            int lca=get_lca(st,ed);
            if((st!=lca)&&(lca!=ed)) ans[i+1]=1ll*siz[st]*siz[ed];
            else{
                int x=st,y=ed;
                if(dep[x]<dep[y])swap(x,y);
                ans[i+1]=1ll*siz[x];
                for(int k=Log;k>=0;k--)
                    if(dep[f[x][k]]>dep[y])
                        x=f[x][k];
                ans[i+1]*=1ll*(n-siz[x]);
            }
        }
        for(int i=0;i<n;i++) ans[i]-=ans[i+1];
        for(int i=0;i<=n;i++)
            printf("%lld ",ans[i]);
        puts("");
    }
    return 0;
}