1. 程式人生 > >[NOIP2018模擬賽10.20A]掛分報告

[NOIP2018模擬賽10.20A]掛分報告

查詢 不用 ble names != 還記得 就是 roi con

閑扯

先看看了B組,T1 ZROI剛好講過一個性質原根一般很小的,直接枚舉;T2一眼二分然後似乎狀壓 T3沒看

然後上來A組題,T1 flow這名字...網絡流?!

T1題面非常的社會主義核心價值觀,看到有個\(m==n\)的限制就想如果去掉怎麽樣,發現一棵樹的話答案是確定的,然後考慮加上那條多出來的邊,發現答案還是不變的?!想了想好像確實是這樣,你樹邊確定了環邊根本不用管,判斷有無解就是點值加起來是否為0.於是直接DFS掃一遍去掉環邊再DFS一遍就好了

T2 題面1984還行 出題人小心啊 掃了一眼覺得好難告辭

T3 第一眼題面 woc!!求求你們給國家省點子彈,我覺得博客中貼出這題題面的也要被查睡標了

第二眼woc?!這不是雅禮集訓講過的原題嗎?!還記得點思路就是預處理坐幾班車最遠可到達的地方,講題人還提到了長鏈剖分

於是肛肛肛...結果死活沒調出來...然後xxzh巨佬講了一種更好寫的暴力....感覺以後考試看到原題還是得想想有沒有其他的思路

結果15+0+0 T1 TM 正負號打反了,又犯SB錯誤 心態崩了

T1 flow

分析

某div 2 F竟這麽水(你還不是掛分了)

見閑扯

代碼

/*
  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;
struct Edge{
    int ne,to,id;
    bool ok;
}edge[maxn<<2];
int h[maxn],num_edge=1;
inline void add_edge(int f,int to,int id){
    edge[++num_edge].ne=h[f];
    edge[num_edge].to=to;
    edge[num_edge].id=id;
    edge[num_edge].ok=0;
    h[f]=num_edge;
}
int w[maxn];
struct Nico{
    int x,y,id,dis;
}nico[maxn<<2];
struct QAQ{
    int x,y,fff;
    int xd,yd;
}yyy[maxn<<2];
int tot=0;
int n,m;
namespace fake{
    ll ans=0;
    bool vis[maxn];
    int fa[maxn];
    void gao_cyc(int now){//去非樹邊
        int v;
        vis[now]=1;
        for(ri i=h[now];i;i=edge[i].ne){
            v=edge[i].to;
            if(v==fa[now])continue;
            if(vis[v]){
                edge[i].ok=edge[i^1].ok=1;
            }
            if(edge[i].ok)continue;
            fa[v]=now;
            gao_cyc(v);
        }   
        return ;
    }
    void get_ans(int now){
        int v,id=0;
        for(ri i=h[now];i;i=edge[i].ne){
            v=edge[i].to;
            if(edge[i].ok)continue;
            if(v==fa[now]){
                id=edge[i].id;continue;
            }
            get_ans(v);
        }
        yyy[++tot].x=now,yyy[tot].y=fa[now];
        yyy[tot].xd=-w[now],yyy[tot].yd=w[now];
        yyy[tot].fff=id;
        w[fa[now]]+=w[now];
        return ;
    }
    void main(){
        int x,y,a,b;
        for(ri i=1;i<=n;i++){
            ans+=w[i];
        }
        memset(vis,0,sizeof(vis));
        if(ans==0)puts("Possible");
        else{ 
            puts("Impossible");
            return ;
        }
        fa[1]=0;
        gao_cyc(1);
        get_ans(1);
        for(ri i=1;i<=tot;i++){
            x=yyy[i].x,y=yyy[i].y;
            a=nico[yyy[i].fff].x,b=nico[yyy[i].fff].y;
            //printf("%d %d %d %d\n",x,y,a,b);
            if(x==a&&y==b){
                nico[yyy[i].fff].dis=-yyy[i].xd;
            }
            else if(x==b&&y==a){
                nico[yyy[i].fff].dis=-yyy[i].yd;
            }
        }
        for(ri i=1;i<=m;i++){
            printf("%d\n",-nico[i].dis);
        }
        return ;
    }
}
int main(){
    int x,y;
    FO(flow);
    read(n);
    for(ri i=1;i<=n;i++)read(w[i]);
    read(m);
    for(ri i=1;i<=m;i++){
        read(x),read(y);
        add_edge(x,y,i);
        add_edge(y,x,i);
        nico[i].x=x,nico[i].y=y,nico[i].dis=0;
    }
    //if(m<=20&&n<=20)bf::main();
    fake::main();
    return 0;
}

T2 moon

T3 car

  • 前置技能點
    • 倍增
    • 掃描線
    • dfs序

預處理每個點在一條鏈上坐\(2^j\)次車最遠到哪裏,這顯然可以倍增搞

然後考慮答案怎麽算

對於詢問\((x,y)\),求出\(z=lca(x,y)\),\(x,y\)先分別跳到距\(z\)最近的點(也就是下次就到\(z\)或更遠),這時候先統計個答案步數\(ans\)

然後發現答案只有兩種情況

Case#1

兩點分別跳一次到\(z\),最終\(ans=ans+2\)

Case#2

設這時候\(x,y\)分別跳到了\(x‘,y‘\)

有一班車覆蓋了路徑\((x‘,y‘)\),那麽答案就是\(ans=ans+1\),因為你只要坐這班車就可以越過LCA到另一個點

考慮怎麽判斷有沒有一班車覆蓋這條路徑,轉化一下變成是否有一班車\((st,ed)\),\(st\)\(x‘\)子樹中,\(ed\)\(y‘\)子樹中(包括\(x‘.y‘\))

如果你看過https://rye-catcher.github.io/2018/10/17/JZOI100019-A-dfs%E5%BA%8F-%E6%89%AB%E6%8F%8F%E7%BA%BF/

就會發現這還可以繼續轉化成\(dfn[x‘]<=dfn[st]<=ed[x‘],dfn[y‘]<=dfn[ed]<=ed[y‘]\)

\((dfn[st],dfn[ed])\)看成一個坐標,發現就是判斷一個矩形中有沒有點

似乎可以在線主席樹做,也好像可以二分套二分,這裏學到了一個新操作樹狀數組+掃描線+二維前綴和

我們轉化後詢問\((x‘,y‘)\)矩形的四個坐標為\((dfn[x‘],dfn[y‘]),(dfn[x‘],ed[y‘]),(ed[x‘],dfn[y‘]),(ed[x‘],ed[y‘])\)

一條班車路徑轉化成一個點\((dfn[st],dfn[ed])\)

我們把這些點放在一起按照掃描線思路,將點按橫坐標進行排序,然後遍歷所有點(優先遍歷班車路徑轉化後的點)

如果是班車路徑的點,加入樹狀數組\([1,dfn[ed]]\)(\(dfn[ed]\)其實就是坐標(x,y)中的\(y\))前綴和

如果是個矩形想要查詢裏面點數咋辦?二維前綴和.

技術分享圖片

我們是將所有點按橫坐標排序的,所以我們可以先加上\(B\)的點數,減去\(A+B\)的點數,然後等到\(ed[x‘]\)坐標時減去\(B+C\) 點數,最後加上整個大面積就是矩形內點數

顯然這些部分直接查詢樹狀數組前綴和就好了

然後有非常多的細節,見代碼註釋吧...

對於樹上路徑,我們都默認dfs序小的為起點方便處理,也算是個技巧

/*
  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++;
}
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=500005;
const int inf=0x7fffffff;
int n,m,q;
struct Edge{
    int ne,to;
}edge[maxn<<1];
int h[maxn],num_edge=1;
inline void add_edge(int f,int to){
    edge[++num_edge].ne=h[f];
    edge[num_edge].to=to;
    h[f]=num_edge;
}
int f[maxn][23];
int dep[maxn],fa[maxn],son[maxn],size[maxn],top[maxn],dfn[maxn],ed[maxn],tot=0;
void dfs_1(int now){
    int v;size[now]=1;
    //if(now==150000)printf("wtf %d\n",now,fa[now]);
    for(ri i=h[now];i;i=edge[i].ne){
        v=edge[i].to;
        if(v==fa[now])continue;
        dep[v]=dep[now]+1,fa[v]=now;
        dfs_1(v);
        size[now]+=size[v];
        if(!son[now]||size[v]>size[son[now]])son[now]=v;
    }
    return ;
}
void dfs_2(int now,int t){
    int v;
    dfn[now]=++tot,top[now]=t;
    if(!son[now]){
        ed[now]=tot;
        return ;
    }
    dfs_2(son[now],t);
    for(ri i=h[now];i;i=edge[i].ne){
        v=edge[i].to;
        if(v==fa[now]||v==son[now])continue;
        dfs_2(v,v);
    }
    ed[now]=tot;
    return ;
}
inline int get_lca(int x,int y){
    int xx=x,yy=y;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])std::swap(x,y);
        x=fa[top[x]];
    }
    //if(!x)printf("--%d %d %d--\n",xx,yy,x);
    if(dep[x]>dep[y])return y;
    return x;
}
void pre_dfs(int now){
    int v;
    for(ri i=h[now];i;i=edge[i].ne){
        v=edge[i].to;
        if(v==fa[now])continue;
        pre_dfs(v);
        if(!f[now][0]||(f[v][0]&&dep[f[now][0]]>dep[f[v][0]]))f[now][0]=f[v][0];//特判 
    }
    return ;
}
struct Seg{
    int x,y,id,d;
    bool operator <(const Seg &rhs)const{
        return (x==rhs.x)?id<rhs.id:x<rhs.x;
    }
}seg[maxn<<2];
int sum[maxn<<2];
inline void update(int x,int k){for(;x<=n;x+=x&(-x))sum[x]+=k;}
inline int query(int x){
    int tmp=0;
    for(;x>=1;x-=x&(-x))tmp+=sum[x];
    return tmp;
}
int fac[maxn],ans[maxn];
int qwq=0,pt[maxn];
int main(){
    int x,y,lca;
    //DEBUG
    read(n);
    fa[1]=0;
    f[1][0]=1;
    for(ri i=2;i<=n;i++){
        f[i][0]=i;
        read(fa[i]);
        add_edge(i,fa[i]),add_edge(fa[i],i);
    }
    dep[1]=0;
    dfs_1(1);
    dfs_2(1,1);
    read(m);
    for(ri i=1;i<=m;i++){
        read(x),read(y);
        if(dfn[x]>dfn[y])std::swap(x,y);
        lca=get_lca(x,y);
        if(!f[x][0]||dep[f[x][0]]>dep[lca])f[x][0]=lca;
        if(!f[y][0]||dep[f[y][0]]>dep[lca])f[y][0]=lca;
        seg[++qwq]=(Seg){dfn[x],dfn[y],0,1};
    }
    pre_dfs(1);
    fac[0]=1;
    for(ri i=1;i<=n;i++)if(f[i][0]==i){
        f[i][0]=0;//註意這裏要置為不可行 
    }
    for(ri k=1;k<=21;k++){
        fac[k]=(fac[k-1]<<1);
        for(ri i=1;i<=n;i++)f[i][k]=f[f[i][k-1]][k-1];
    }
    //for(ri k=0;k<=2;k++)for(ri i=1;i<=n;i++)printf("%d %d %d\n",i,k,f[i][k]);
    int po,qo;
    read(q);
    for(ri o=1;o<=q;o++){
        read(x),read(y);
        if(dfn[x]>dfn[y])std::swap(x,y);
        lca=get_lca(x,y);
        po=x,qo=y;
        for(ri i=21;i>=0;i--)
            if(dep[f[po][i]]>dep[lca]){
                po=f[po][i];
                ans[o]+=fac[i];
            }
        for(ri i=21;i>=0;i--)
            if(dep[f[qo][i]]>dep[lca]){
                qo=f[qo][i];
                ans[o]+=fac[i];
            }
        if((!f[po][0]&&po!=lca)||(!f[qo][0]&&qo!=lca)){//註意!! 
            ans[o]=-1;
        }
        else {
            if(po==lca||qo==lca){
                ans[o]++;
                //printf("--%d %d--\n",o,ans[o]);
            }
            else {
                ans[o]+=2;
                //printf("--%d %d--\n",o,ans[o]);   
                seg[++qwq]=(Seg){dfn[po]-1,dfn[qo]-1,o,1};//二維前綴和 
                seg[++qwq]=(Seg){ed[po],ed[qo],o,1};
                seg[++qwq]=(Seg){dfn[po]-1,ed[qo],o,-1};
                seg[++qwq]=(Seg){ed[po],dfn[qo]-1,o,-1};
            }
        }
    }
    std::sort(seg+1,seg+1+qwq);
    for(ri i=1;i<=qwq;i++){
        if(!seg[i].id){
            update(seg[i].y,seg[i].d);
        }
        else{ 
            pt[seg[i].id]+=seg[i].d*query(seg[i].y);
        }
    }
    for(ri i=1;i<=q;i++){
        if(pt[i])printf("%d\n",ans[i]-1);
        else printf("%d\n",ans[i]);
    }
    return 0;
}

[NOIP2018模擬賽10.20A]掛分報告