Problem A: 种树 解题报告

Problem A: 种树

Description

很久很久以前,一个蒟蒻种了一棵会提问的树,树有\(n\)个节点,每个节点有一个权值,现在树给出\(m\)组询问,每次询问两个值:树上一组点对\((x,y)\)简单路径上不同权值的数量以及权值的\(\max\)为多少

然而某一天毒瘤胖子过来给树浇了点水,询问变成了每次求,一组点对\((x,y)\)简单路径上的不同权值的数量以及权值的\(mex\)

然而又过了两天毒瘤袁稳稳也过来给树浇了点水,询问变成了每次求若干组点对\((x,y)\)简单路径的并集上的不同权值的数量以及权值的\(mex\)(如有疑惑见\(HINT\))

然后蒟蒻就菜哭在树下了qwq并且毫不负责任地把这个问题丢给了刚好路过的你

因为这棵树受到了两个毒瘤的祝福,每次询问受到了加密,记\(lastans\)表示上一次询问的两个答案的和,这次询问中的读入的表示点对的两个数都要\(xor \ (lastans?op)\),其中\(op\in \{0,1\}\),具体见数据范围

后话:然而毕竟是蒟蒻种的树,毒瘤的祝福并没有使这题送温暖的本质发生变化qwq

Input

第一行三个整数\(n,m,op\)

接下来一行\(n\)个整数表示每个节点的权值\(val_i\)

再接下来\(n?1\)行每行两个整数\(x,y\)表示树上的一条边

再接下来\(m\)组询问,每组询问第一行一个整数\(num\)表示点对的数量,接下来\(num\)行每行两个整数\((x,y)\)表示一组点对

Output

对于每组询问,输出一行两个整数分别表示不同权值的数量以及权值的\(mex\)

Sample Input

5 5 0
2 0 0 1 3
1 2
2 3
2 4
4 5
1 4 5
3 1 5 5 2 4 4
2 2 4 2 4
4 2 5 3 1 4 3 2 5
1 2 5 

Sample Output

2 0
4 4
2 2
4 4
3 2

HINT

一些你可能根本不需要用到的说明:一个数集\(S\)的\(mex\)为最小的满足\(x\notin S\)的非负整数\(x\)

\(subtask1(20\%)\):\(n,m≤1000,\sum num≤1000,op=0\)

\(subtask2(30\%)\):\(n,m≤10^5,\sum num≤10^5\),树是一条链,\(op=0\)

\(subtask3(50\%)\):\(n,m≤10^5,\sum num≤10^5,0≤val_i≤30000\)



完 全 没 有 感 受 到 温 暖,虽 然 确 实 是 最 简 单 的 一 道,剩 下 两 道 我 改 不 出 来

30000这个数我们很容易除上个64哎

然后随便用树剖倍增之类的维护一下,发现单次操作复杂度达到了惊人的\(\log n\frac{val}{64}\),显然没救了。

这时候就是分块出场的时候辣

因为询问的是链的信息,所以我们考虑对树的深度进行分块,既对树提取一定的关键点,相邻的关键点深度差不超过\(\sqrt n\)就可以了,这样我们就有了\(\sqrt n\)个关键点。

然后我们拿关键点拼吗?复杂度达到了更惊人的\(\sqrt n \frac{val}{64}\)

所以考虑先预处理在同一条到跟路径上关键点的路径信息,这里采用手写\(bitset\)的方法就可以做到\(O(1)\)整数与上\(bitset\)了,然后每个关键点向上与顺便更新一下就行了。

查询的时候,不完整的暴力跳,完整的做一次bitset之间的与运算就行了。

复杂度:\(O(n\sqrt n+q(\sqrt n+\frac{val}{64}))\)



Code:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ull unsigned long long
const int N=1e5+10,B=470;
const ull full=~0ull,cut=(1ull<<16)-1;
int ct[cut+1],n,m,op;
int cal(ull x){return ct[x&cut]+ct[x>>16&cut]+ct[x>>32&cut]+ct[x>>48&cut];}
using std::max;
struct Bitset
{
    ull dx[B];
    int len;
    void clear(){memset(dx,0,sizeof(dx)),len=0;}
    Bitset(){clear();}
    void friend operator |=(Bitset &A,int x){A.dx[x>>6]|=1ull<<(x&63);A.len=max(A.len,x>>6);}
    void friend operator |=(Bitset &A,Bitset B)
    {
        A.len=A.len>B.len?A.len:B.len;
        for(int i=0;i<=A.len;i++) A.dx[i]|=B.dx[i];
    }
    int count()
    {
        int ret=0;
        for(int i=0;i<=len;i++)
            ret+=cal(dx[i]);
        return ret;
    }
    int mex()
    {
        for(int i=0;i<=len;i++)
        {
            if(dx[i]==full) continue;
            for(int j=0;j<64;j++)
                if(!(dx[i]>>j&1))
                    return (i<<6)+j;
        }
        return 233;
    }
}path[320][320],ans;
int head[N],to[N<<1],Next[N<<1],cnt;
void add(int u,int v)
{
    to[++cnt]=v,Next[cnt]=head[u],head[u]=cnt;
}
int f[N][19],dep[N],mxdep[N],id[N],rt[N],val[N],pre[N],H;
void dfs(int now)
{
    for(int i=1;f[now][i-1];i++) f[now][i]=f[f[now][i-1]][i-1];
    dep[now]=dep[f[now][0]]+1;
    mxdep[now]=1;
    for(int v,i=head[now];i;i=Next[i])
        if((v=to[i])!=f[now][0])
        {
            f[v][0]=now;
            dfs(v);
            mxdep[now]=max(mxdep[now],mxdep[v]+1);
        }
    if(mxdep[now]==H||now==1)
    {
        rt[id[now]=++rt[0]]=now;
        mxdep[now]=0;
    }
}
int LCA(int x,int y)
{
    if(dep[x]<dep[y]) return LCA(y,x);
    for(int i=18;~i;i--)
        if(dep[f[x][i]]>=dep[y])
            x=f[x][i];
    if(x==y) return x;
    for(int i=18;~i;i--)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];
}
void query(int x,int y)
{
    while(!id[x]&&x!=y) ans|=val[x],x=f[x][0];
    if(x==y) {ans|=val[x];return;}
    int s=x;
    while(dep[pre[x]]>dep[y]) x=pre[x];
    ans|=path[id[s]][id[x]];
    while(dep[f[x][0]]>=dep[y]) x=f[x][0],ans|=val[x];
}
void Query(int x,int y)
{
    int lca=LCA(x,y);
    query(x,lca);
    query(y,lca);
}
int main()
{
    scanf("%d%d%d",&n,&m,&op);
    for(int i=1;i<=cut;i++) ct[i]=ct[i>>1]+(i&1);
    H=sqrt(n)+1;
    for(int i=1;i<=n;i++) scanf("%d",val+i);
    for(int u,v,i=1;i<n;i++) scanf("%d%d",&u,&v),add(u,v),add(v,u);
    dfs(1);
    for(int i=1;i<=rt[0];i++)
    {
        Bitset tmp;
        tmp|=val[rt[i]];
        path[i][i]=tmp;
        for(int now=f[rt[i]][0];now;now=f[now][0])
        {
            tmp|=val[now];
            if(id[now])
            {
                path[i][id[now]]=tmp;
                if(!pre[rt[i]]) pre[rt[i]]=now;
            }
        }
    }
    for(int lastans=0,num,x,y,i=1;i<=m;i++)
    {
        scanf("%d",&num);
        ans.clear();
        for(int j=1;j<=num;j++)
        {
            scanf("%d%d",&x,&y);
            x^=lastans*op,y^=lastans*op;
            Query(x,y);
        }
        int t1=ans.count(),t2=ans.mex();
        lastans=t1+t2;
        printf("%d %d\n",t1,t2);
    }
    return 0;
}


2019.1.6

原文地址:https://www.cnblogs.com/ppprseter/p/10229984.html

时间: 2024-10-22 22:57:35

Problem A: 种树 解题报告的相关文章

ZOJ Problem Set - 1025解题报告

ZOJ Problem Set - 1025 题目分类:动态规划 原题地址:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1025   题目大意就是有很多木头,都有各自的长度和重量.现在要加工这些木头,如果加工某根木头的长度和重量大于等于它上一根木头的长度和重量,那么加工它不需要时 间,否则要花1分钟.现给出一堆木头的长度和重量,要求加工完这堆木头可以花的最少时间.例如给出5根木头长度重量分别为(4,9), (5,2),

[COGS1862]种树 解题报告

[问题描述] A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府决定沿圆形广场外圈种一圈树.园林部门得到指令后,初步规划出n个种树的位置,顺时针编号1到n.并且每个位置都有一个美观度Ai,如果在这里种树就可以得到这Ai的美观度.但由于A城市土壤肥力欠佳,两棵树决不能种在相邻的位置(i号位置和i+1号位置叫相邻位置.值得注意的是1号和n号也算相邻位置!). 最终市政府给园林部门提供了m棵树苗并要求全部种上,请你帮忙设计种树方案使得美观度总和最大.如果无法将m棵树苗全部种上,给出无解信息.

Problem - 433C - Codeforces解题报告

对于这题本人刚开始的时候的想法是:先把最大两数差的位置找到然后merge计算一个值再与一连串相同的数做merge后计算一个值比较取最大值输出:可提交后发现不对,于是本人就搜了一下正解发现原来这题的正确解题思路是:采用数学中的中位数原理,分别把某数两边相邻且不同的数存入向量容器Vector中然后排序,找到中位数计算一遍,找到计算的最大值,然后把按照输入顺序的值计算出一个总和,然后相减就是其解. 关于中位数原理本人稍微提一下: 求中位数,首先要先进行数据的排序(从小到大),然后计算中位数的序号,分数

BestCoder18 1002.Math Problem(hdu 5105) 解题报告

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5105 题目意思:给出一个6个实数:a, b, c, d, l, r.通过在[l, r]中取数 x,使得这个函数 f(x)= |a∗x3+b∗x2+c∗x+d| 最大. 我一开始做的时候,很天真的认为数据量这么小,一个一个试,暴力搜就肯定能得到答案啦.但是一个很严重的问题是,x 没有说是实数还是整数,所以枚举根本不可行. 于是就联想到应该使用高中求一元三次方程的方法来做.我当时是直接从 f(l), f

POJ 3468.A Simple Problem with Integers 解题报告

用树状数组和线段树会比较简单,这里用这道题来学习Splay. 第一次写,代码比较丑 /* 初始化添加一个key值足够大的结点 保证每个需要的结点都有后继 */ #include <iostream> #include <cstdio> #define ll long long using namespace std; const int MAXN = 1111111, INF = 0x7fffffff; struct node { //需要的记录信息 ll key, val, su

XVIII Open Cup named after E.V. Pankratiev. Grand Prix of Khamovniki Problem J Stairways解题报告(分块+维护凸壳)

首先ORZ一发Claris聚聚的题解:http://www.cnblogs.com/clrs97/p/8689215.html,不然我可能没机会补过这道神题了. 这里写一个更详细的题解吧(我还是太菜了啊). 题目描述 有\(n(n \le10^5)\)个人依次进入一个入口,要到一个出口.入口到出口有两条同样长的路.每个人都有一个速度,用通行时间\(a_i(1\le a_i \le 10^6)\)表示,他可以选择任一条路走.但是,若走这条路的前面的人比他慢的话,他只能降到和前面所有人最慢的那个人同

hdu - 5349 MZL&#39;s simple problem(解题报告)

A - MZL's simple problem Time Limit:1500MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Status Description A simple problem Problem Description You have a multiple set,and now there are three kinds of operations: 1 x : add number

解题报告 之 HDU5328 Problem Killer

解题报告 之 HDU5328 Problem Killer Description You are a "Problem Killer", you want to solve many problems. Now you have  problems, the -th problem's difficulty is represented by an integer  (). For some strange reason, you must choose some integer  

[poj 2480] Longge&#39;s problem 解题报告 (欧拉函数)

题目链接:http://poj.org/problem?id=2480 题目大意: 题解: 我一直很欣赏数学题完美的复杂度 #include<cstring> #include<algorithm> #include<cstdio> #include<iostream> #include<cmath> using namespace std; typedef long long ll; const int N=(1<<31)+15;