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

附题:
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 (tmpint 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<
        //for (int i=1;i<=tot;i++)
        //cout<
        //cout<
        //cout<
        sort(dis+1,dis+1+tot);
        int l=1,r=tot;
        while(lwhile (dis[l]+dis[r]>k && lreturn 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;isize();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
#include
#include
#include
#include
#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 (tmpx;
    }
}
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 (lelse 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<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<*a[rt]<<' '<//cout<//cout<if (flag[tmp]!=xx) continue;
            //cout<' '<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<' '<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;iint x,y;
            scanf("%d%d",&x,&y);
            addedge(x,y);
        }
        //cout<<(ni[20]*60)%mmod<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,写这点东西让我调格式与版式写了一小时,唉。。。

你可能感兴趣的:(oi之路)