• [NOIP2018模拟赛10.20A]挂分报告


    闲扯

    先看看了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
    ",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
    ",-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))前缀和

    如果是个矩形想要查询里面点数咋办?二维前缀和.

    233

    我们是将所有点按横坐标排序的,所以我们可以先加上(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
    ",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--
    ",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
    ",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--
    ",o,ans[o]);
                }
                else {
                    ans[o]+=2;
                    //printf("--%d %d--
    ",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
    ",ans[i]-1);
            else printf("%d
    ",ans[i]);
        }
        return 0;
    }
    
  • 相关阅读:
    判断奇偶数2
    判断奇偶数
    15.09.29
    .
    Java代码
    Handler 接收Parcelable ArrayList时返回空的错误
    Android Binder设计与实现
    xml解析代码示例
    解析rss和atom文件出现乱码问题
    使用Html.fromHtml将html格式字符串应用到textview上面
  • 原文地址:https://www.cnblogs.com/Rye-Catcher/p/9823554.html
Copyright © 2020-2023  润新知