【蒟蒻的点分治专题训练】----5道题题解

来源:互联网 发布:windows.old可以移动 编辑:程序博客网 时间:2024/06/09 21:29

附题:
poj1741,hdu4812,codeforces161D,bzoj3697,bzoj2152
先来一波总结:
好好做了这几道题之后,发现树上的点分治几乎可以说是模板题,每道题改变的地方都只有处理 过当前子树根节点的路径,求重心,求距离几乎都没有什么太大改变,(写了五遍简直要写吐),差不多就是如下格式:

void dfs(int x){    求x所在子树的重心 root    (记得 用临时变量来存每次求出的root,因为root身为一个全局变量,之后的求重心过程中会改变root的值)    int rt=root;        vis[rt]=1;    根据题意处理这个子树中过rt的路径    结束}

第一遍的时候,细节各种恶心,有的地方要调好长时间才能发现细节性的错误,有的题还要卡vector,蒟蒻决定以后时间不是特别紧的情况下舍弃vector,用手写边表orz。

至于树的重心,感觉这篇文章写的可以,帮大忙了:
http://blog.csdn.net/xdu_truth/article/details/9104629

多个子树求重心:

void dfssize(int x,int fa){    size[x]=1;    for (int i=head[x];i!=0;i=edge[i].next)    {        int v=edge[i].v;        if (vis[v]!=1&&v!=fa)        {            dfssize(v,x);            size[x]+=size[v];        }    }}void dfsroot(int x,int fa,int num){    int tmp=-1;    for (int i=head[x];i!=0;i=edge[i].next)    {        int v=edge[i].v;        if (vis[v]!=1&&v!=fa)        {            dfsroot(v,x,num);            tmp=max(tmp,size[v]);        }    }    tmp=max(tmp,num-size[x]);    if (tmp<bijiao)    {        bijiao=tmp;        root=x;    }}void dfs(int x){    bijiao=n+1;    dfssize(x,x);    dfsroot(x,x,size[x]);    int rt=root;vis[rt]=1;........................}

差不多就是这样了,写的丑就丑吧。第一道题:poj1741.这道题也计算出根节点到所有当前子树节点的距离,存在一个数组之中,然后sort。。。用奇技淫巧的方法可已将排序之后的距离们O(n)的处理出有多少小于k的路径:
但是这会把不过根节点的路径重复计算,因此还要减去子树中的路径,具体看代码:

int calc(int x,int fa,int d){    int ret=0;    tot=0;dfsdis(x,fa,d);        //cout<<rt<<' '<<tot<<endl;        //for (int i=1;i<=tot;i++)        //cout<<dis[i]<<' ';        //cout<<endl;        //cout<<ans<<endl;        sort(dis+1,dis+1+tot);        int l=1,r=tot;        while(l<r)        {                while (dis[l]+dis[r]>k && l<r)                r--;                ret+=r-l;                l++;        }    return ret;}void dfs(int x){    bijiao=9999999;    dfssize(x,x);    dfsroot(x,x,size[x]);    int rt=root;    vis[rt]=1;    ans+=calc(rt,rt,0);    for (int i=0;i<lin[rt].size();i++)    {        int v=lin[rt][i].v;        if (vis[v]==0)        {            ans-=calc(v,rt,lin[rt][i].d);            dfs(v);        }       }   }

(鉴于有五道题,我就不一一附完整代码了。。。。)
接下来一道题:
hdu4812
这道题和上道题略有不同,即问有多少条路径,符合路径上所有节点的权值乘积模1000003等于k,呵呵呵,
刚一看题,woca,这难道不是上一道题的简化版么,等于k好简单,然后分分钟打脸,不得已看了别人的题解,哎,满满都是泪,这道题方法不能像上一道题一样,要用一个新方法来计算。
建议先不做这道题,简直恶心的可以,不禁要注意long long这种变量类型,还要预处理出逆元,还要卡一次蒟蒻们的vector,还要开c++的栈优化,跳了好长时间,这道题必须要附代码,以此明此题恶心:

#include<iostream>#include<algorithm>#include<stdio.h>#include<vector>#include<string.h>#define maxn (2*100005)#define mmod (1000003)#define mm (2*1000010)#pragma comment(linker,"/STACK:102400000,102400000")using namespace std;#pragma comment(linker,"/STACK:102400000,102400000")typedef long long ll;struct node{    int v,next;}edge[maxn*2];int head[maxn*2],tot2;void addedge(int ui,int vi){    edge[tot2].v=vi;    edge[tot2].next=head[ui];    head[ui]=tot2++;    edge[tot2].v=ui;    edge[tot2].next=head[vi];    head[vi]=tot2++;}int root,tot,xx,n,has[mm],id[maxn],flag[mm],ansl,ansr;int bijiao,size[maxn],vis[maxn];ll dis[maxn],a[maxn],ni[mm],k;inline ll read(){    ll x=0;char ch=getchar();    while(ch<'0'||ch>'9')ch=getchar();    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x;}void ext_gcd(ll a,ll b,ll &x,ll &y){    if (b==0)    {        x=1;y=0;        return ;    }    else    {        ext_gcd(b,a%b,x,y);        int t=x;x=y;           y=t-a/b*y;        return ;    }}void dfssize(int x,int fa){    size[x]=1;    for (int i=head[x];i!=0;i=edge[i].next)    {        int v=edge[i].v;        if (vis[v]!=1&&v!=fa)        {            dfssize(v,x);            size[x]+=size[v];        }    }}void dfsroot(int x,int fa,int num){    int tmp=-1;    for (int i=head[x];i!=0;i=edge[i].next)    {        int v=edge[i].v;        if (vis[v]!=1&&v!=fa)        {            dfsroot(v,x,num);            tmp=max(tmp,size[v]);        }    }    tmp=max(tmp,num-size[x]);    if (tmp<bijiao)    {        bijiao=tmp;        root=x;    }}void dfsdis(int x,int fa,ll p){    tot++;    dis[tot]=p*a[x]%mmod;    id[tot]=x;    ll tmp=dis[tot];    for (int i=head[x];i!=0;i=edge[i].next)    {        int v=edge[i].v;        if (vis[v]!=1&&v!=fa)        dfsdis(v,x,tmp);    }}void getans(int l,int r){    if (l>r) swap(l,r);    if (l<ansl) ansl=l,ansr=r;    else if (l==ansl) ansr=min(ansr,r);    return ;}void dfs(int x){    bijiao=99999999;    dfssize(x,x);    dfsroot(x,x,size[x]);    int rt=root;    vis[rt]=1;    xx++;    //cout<<rt<<endl;    for (int j=head[rt];j!=0;j=edge[j].next)    {        int v=edge[j].v;        if (vis[v]==1) continue;        tot=0;            dfsdis(v,rt,1);        for (int i=1;i<=tot;i++)        {            if ((dis[i]*a[rt])%mmod==k) getans(id[i],rt);            ll tmp=((ni[(dis[i]*a[rt])%mmod])*k)%mmod;            //cout<<dis[i]*a[rt]<<' '<<tmp<<endl;            //cout<<flag[tmp]<<endl;            //cout<<xx<<endl;            if (flag[tmp]!=xx) continue;            //cout<<has[tmp]<<' '<<id[i]<<endl;            getans(has[tmp],id[i]);        }        for (int i=1;i<=tot;i++)        {            if ((flag[dis[i]]!=xx)||(has[dis[i]]>id[i]))            has[dis[i]]=id[i],flag[dis[i]]=xx;            //cout<<dis[i]<<' '<<id[i]<<endl;        }    }    for (int i=head[rt];i!=0;i=edge[i].next)    {        int v=edge[i].v;        if (vis[v]!=1)        dfs(v);    }}int main(){        for (int i = 0;i < mmod;i++) {          ll y;            ext_gcd(i*1ll, mmod*1ll, ni[i], y);              ni[i] %= mmod, ni[i] = (ni[i]+mmod)%mmod;          }      while(scanf("%d%I64d",&n,&k)!=EOF)    {        xx=0;tot2=1;        memset(flag,0,sizeof(flag));        memset(has,0,sizeof(has));        memset(head,0,sizeof(head));        for (int i=1;i<=n;i++)        {            a[i]=read();            vis[i]=0;        }        for (int i=1;i<n;i++)        {            int x,y;            scanf("%d%d",&x,&y);            addedge(x,y);        }        //cout<<(ni[20]*60)%mmod<<endl;        ansl=9999999,ansr=9999999;        dfs(1);        if (ansl==9999999&&ansr==9999999)        printf("No solution\n");        else        printf("%d %d\n",ansl,ansr);    }}

接下来两道题是codeforces161D,bzoj2152,这两道题与hdu4812是同一个类型,在做过那种恶心题后感觉这两道题都很简单,很容易就写出来并且A掉了,不过细节好还是要注意的。
最后是bzoj3697,这道题的思路很巧妙,和前几道题的做法不同,表示自己智商不够,只好看了hzwer聚聚的博客,自认为没有人家写得好就不附代码了。。呵呵呵。。

总之点分治的题就写这么几道了,
顺便说一句这博客编辑器真是恶心,不知道出了bug,写这点东西让我调格式与版式写了一小时,唉。。。

0 0
原创粉丝点击