poj3764解题报告

来源:互联网 发布:android6.0 相机源码 编辑:程序博客网 时间:2024/06/11 20:11

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">这是第一次写博客发解题报告,当然我目前也还是个大水逼,所以代码写的也不太好,见谅。</span>

这道题就是要求在树中找两个结点,且两个结点之间路径唯一,求最长的异或路径。很明显不能用暴力,O(N2)时间复杂度100000个点。首先我们需要知道一个性质:a^b = (a^c)^(b^c),这样就可以考虑找出ab公共的c,实际上就是求出从根节点到每个节点的异或值,这样任意两个点做异或,即是他们之间的异或路径(相同部分异或抵消了)。先遍历DFS遍历一遍,找出所有结点到树根的路径异或值,则问题就转化成了求这些点中任意两个点的异或值,接下来就很简单了,将DFS所得的每个点的异或值转化成二进制数存进字典树,然后从高位至低位对字典树进行贪心,找出最大的那个值。字典树的时间复杂度为O(31*n),dfs时间复杂度为O(n)。

这道题字典树最多有3100000个点,所以容易TLE,一定要进行剪枝,推荐使用静态数组存字典树,但我是用的动态开辟数组存树,所以过的十分勉强。

另外这题也让我长了点姿势,位运算1<<31会溢出,楼主确实还很水,因为这个WA了两次。。。快哭了

/**memory   27468KB    time    1282ms  */#include<cstdio>#include<cstring>#include<iostream>using namespace std;const int maxn=100010;int cnt,sum,flag;int dis[maxn],head[maxn],s[maxn][33];struct edge{    int v,w;    int next;}edge[2*maxn];struct node{    node *a[2];    int p;};void addedge(int u,int v,int w){    edge[cnt].v=v;  edge[cnt].w=w;  edge[cnt].next=head[u]; head[u]=cnt++;    edge[cnt].v=u;  edge[cnt].w=w;  edge[cnt].next=head[v]; head[v]=cnt++;}void dfs(int u,int fa){    for(int i=head[u];i!=-1;i=edge[i].next){        int v=edge[i].v;        if(v==fa)   continue;        dis[v]=dis[u]^edge[i].w;        dfs(v,u);    }}void insert(int k,int m,node *h){    h->p++;    if(k>30) return;    if(h->a[s[m][k]]!=NULL)        insert(k+1,m,h->a[s[m][k]]);    else{        for(int i=k;i<=30;i++){            node*t=new node;            t->p=1;            for(int j=0;j<2;j++)                t->a[j]=NULL;            h->a[s[m][i]]=t;            h=t;        }    }}void find(int k,int m,node*h){    if(h->a[!s[m][k]]!=NULL){        sum+=(1<<(30-k));        find(k+1,m,h->a[!s[m][k]]);    }    else if(h->a[s[m][k]]!=NULL)        find(k+1,m,h->a[s[m][k]]);    else        return;}int main(){    //freopen("xor.txt","r",stdin);    int u,v,w,n;    while(~scanf("%d",&n)){        node *h=new node; h->p=0;        for(int i=0;i<2;i++)            h->a[i]=NULL;        cnt=0;        memset(head,-1,sizeof(head));        for(int i=1;i<n;i++){            scanf("%d %d %d",&u,&v,&w);            addedge(u,v,w);        }        dis[0]=0;        dfs(0,-1);        int maxdis=0;        for(int i=0;i<n;i++)            maxdis=max(maxdis,dis[i]);        for(flag=0;flag<31;flag++)            if(maxdis<(1<<flag))  break;            int ans=0;        for(int i=0;i<n;i++){            int dist=dis[i];            for(int j=30;j>=0;j--){                s[i][j]=dist%2;                dist=dist>>1;            }            insert(0,i,h);      //注意这里是每insert一次,就find一次,不要把insert和find放到两个循环里,避免重复贪心。            sum=0;            find(0,i,h);            ans=max(ans,sum);            if(ans==(1<<flag)-1)    //flag是一个剪枝,如果去掉就会TLE                break;        }        printf("%d\n",ans);    }}


0 0
原创粉丝点击