zoukankan      html  css  js  c++  java
  • (淀粉质)P2634 [国家集训队]聪聪可可 and P3806 多次离线查询树上距离为k的点对是否存在

    一句话点分治:每次找到树的重心,暴力或者不暴力处理以这个重心为分界点的 各个子树 之间 产生的贡献,然后删除这个点,产生很多树,对每棵子树重复上述操作。。。

    有点像dsu on tree,都是通过均摊来实现nlogn


    P3806

    提供一种离线处理询问的做法:

    点分的过程中在对root的单独处理时,每处理一个子树,就用由root其他子树构成的桶去离线处理询问是否存在。最后处理完root的所有子树相互之间产生的贡献后,把桶清空。

    void cal(int from)
    {
        tmp.clear();
        int last, now;
        for (int i = head[from]; ~i; i = e[i].next)
        {
            int to = e[i].to, cost = e[i].cost;
            if (vis[to])continue;
            last = tmp.size();
            //getdis是把子树中得到的路径长度加入tmp中,并得到tmp中的起点last和终点now(暂时不clear,留着最后清空桶)
            getdis(to, from, cost);
            now = tmp.size() - 1;
            //makeans
            for (int i = 1; i <= m; i++)
            {
                int ans = q[i];//当前询问的值
                if (ok[i])continue;//已存在,跳过
                for (int j = last; j <= now; j++)//遍历当前的子树可以得到的路径长度
                    if (ans >= tmp[j] && cnt[ans - tmp[j]])//桶中有这样的值
                    {
                        ok[i] = 1;//存在
                        break;
                    }
            }
            //insert_cnt
            for (int i = last; i <= now; i++)
                if (tmp[i] <= 10000000)
                    cnt[tmp[i]] = 1;
        }
        //clear_cnt
        for (auto i : tmp)
            if (i <= 10000000)
                cnt[i] = 0;
    }
    

    求重心:

    void find(int from, int fa)//找重心
    {
        siz[from] = 1, mx[from] = 0;
        for (int i = head[from]; ~i; i = e[i].next)
        {
            int to = e[i].to;
            if (to == fa || vis[to])continue;
            find(to, from);
            siz[from] += siz[to];
            mx[from] = max(mx[from], siz[to]);
        }
        mx[from] = max(mx[from], tot - siz[from]);
        if (mx[from] < mx[root])
            root = from;
    }
    

    点分:

    void divid(int from)
    {
        vis[from] = 1;
        cal(from);
        for (int i = head[from]; ~i; i = e[i].next)
        {
            int to = e[i].to;
            if (vis[to])continue;
            tot = siz[to];
            root = 0;
            find(to, from);
            divid(root);
        }
    }
    

    最终代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
    using namespace std;
    
    const ll mod = 998244353;
    const int maxn = 1e5 + 5;
    const int inf = 1e9 + 7;
    const ll lnf = 1e18;
    
    struct edge {
        int to, cost, next;
    }e[maxn << 1];
    
    int edge_cnt = 0, head[maxn];
    void add(int from, int to,int cost)
    {
        e[++edge_cnt] = { to,cost, head[from] };
        head[from] = edge_cnt;
    }
    
    int siz[maxn], tot, mx[maxn];
    int root = 0;
    bool vis[maxn];
    
    int n, m;
    
    void find(int from, int fa)//找重心
    {
        siz[from] = 1, mx[from] = 0;
        for (int i = head[from]; ~i; i = e[i].next)
        {
            int to = e[i].to;
            if (to == fa || vis[to])continue;
            find(to, from);
            siz[from] += siz[to];
            mx[from] = max(mx[from], siz[to]);
        }
        mx[from] = max(mx[from], tot - siz[from]);
        if (mx[from] < mx[root])
            root = from;
    }
    
    int q[105], ok[105];
    
    bool cnt[10000005];
    vector<int>tmp;
    
    void getdis(int from, int fa, int res)
    {
        tmp.push_back(res);
        for (int i = head[from]; ~i; i = e[i].next)
        {
            int to = e[i].to, cost = e[i].cost;
            if (vis[to] || to == fa)continue;
            getdis(to, from, res + cost);
        }
    }
    
    void cal(int from)
    {
        tmp.clear();
        int last, now;
        for (int i = head[from]; ~i; i = e[i].next)
        {
            int to = e[i].to, cost = e[i].cost;
            if (vis[to])continue;
            last = tmp.size();
            getdis(to, from, cost);
            /*for (auto i : tmp)
                cout << i << " ";
            cout << endl;*/
            now = tmp.size() - 1;
    
            //makeans
            for (int i = 1; i <= m; i++)
            {
                int ans = q[i];//当前询问的值
                if (ok[i])continue;//已存在,跳过
                for (int j = last; j <= now; j++)//遍历当前的子树可以得到的路径长度
                    if (ans >= tmp[j] && cnt[ans - tmp[j]])//桶中有这样的值
                    {
                        ok[i] = 1;//存在
                        break;
                    }
            }
            //insert_cnt
            for (int i = last; i <= now; i++)
                if (tmp[i] <= 10000000)
                    cnt[tmp[i]] = 1;
        }
        //clear_cnt
        for (auto i : tmp)
            if (i <= 10000000)
                cnt[i] = 0;
    }
    
    void divid(int from)
    {
        vis[from] = 1;
        cal(from);
        for (int i = head[from]; ~i; i = e[i].next)
        {
            int to = e[i].to;
            if (vis[to])continue;
            tot = siz[to];
            root = 0;
            find(to, from);
            divid(root);
        }
    }
    
    int main()
    {
        fastio;
        memset(head, -1, sizeof(head));
        cin >> n >> m;
        for (int i = 1; i < n; i++)
        {
            int x, y, z;
            cin >> x >> y >> z;
            add(x, y, z);
            add(y, x, z);
        }
        for (int i = 1; i <= m; i++)
            cin >> q[i];
        mx[0] = n;
        cnt[0] = 1;
        root = 0;
        find(1, 0);
        divid(root);
        for (int i = 1; i <= m; i++)
            if (ok[i])
                cout << "AYE" << endl;
            else
                cout << "NAY" << endl;
        return 0;
    
    }
    

    P2634 [国家集训队]聪聪可可

    模版题,没有任何其他操作,直接开个cnt[3]来点分算贡献就行了。

    需要注意一点,除了root子树之间的贡献之外,还有对于root子树上的某个点x,x到root,x和x的贡献。

    #include<bits/stdc++.h>
    #define ll long long
    #define pii pair<int,int>
    #define pll pair<ll,ll>
    #define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
    using namespace std;
    
    const ll mod = 998244353;
    const int maxn = 1e5 + 5;
    const int inf = 1e9 + 7;
    const ll lnf = 1e18;
    
    struct edge {
        int to, cost, next;
    }e[maxn << 1];
    
    int edge_cnt = 0, head[maxn];
    void add(int from, int to,int cost)
    {
        e[++edge_cnt] = { to,cost, head[from] };
        head[from] = edge_cnt;
    }
    
    int siz[maxn], tot, mx[maxn];
    int root = 0;
    bool vis[maxn];
    
    int n, m;
    
    void find(int from, int fa)//找重心
    {
        siz[from] = 1, mx[from] = 0;
        for (int i = head[from]; ~i; i = e[i].next)
        {
            int to = e[i].to;
            if (to == fa || vis[to])continue;
            find(to, from);
            siz[from] += siz[to];
            mx[from] = max(mx[from], siz[to]);
        }
        mx[from] = max(mx[from], tot - siz[from]);
        if (mx[from] < mx[root])
            root = from;
    }
    
    int cnt[4];
    vector<int>tmp;
    
    void getdis(int from, int fa, int res)
    {
        tmp.push_back(res % 3);
        for (int i = head[from]; ~i; i = e[i].next)
        {
            int to = e[i].to, cost = e[i].cost;
            if (vis[to] || to == fa)continue;
            getdis(to, from, res + cost);
        }
    }
    
    int res = 0;
    
    void cal(int from)
    {
        tmp.clear();
        int last, now;
        for (int i = head[from]; ~i; i = e[i].next)
        {
            int to = e[i].to, cost = e[i].cost;
            if (vis[to])continue;
            last = tmp.size();
            getdis(to, from, cost);
            now = tmp.size() - 1;
            //makeans
            for (int i = last; i <= now; i++)
            {
                if (tmp[i] == 1)
                    res += 2 * cnt[2];
                else if (tmp[i] == 2)
                    res += 2 * cnt[1];
                else
                    res += 2 * cnt[0] + 2;//加2是root和x以及x和root
            }
            //insert_cnt
            for (int i = last; i <= now; i++)
                cnt[tmp[i]]++;
        }
        //clear_cnt
        cnt[0] = cnt[1] = cnt[2] = 0;
    }
    
    void divid(int from)
    {
        vis[from] = 1;
        cal(from);
        for (int i = head[from]; ~i; i = e[i].next)
        {
            int to = e[i].to;
            if (vis[to])continue;
            tot = siz[to];
            root = 0;
            find(to, from);
            divid(root);
        }
    }
    
    int gcd(int a, int b)
    {
        return b ? gcd(b, a % b) : a;
    }
    
    int main()
    {
        fastio;
        memset(head, -1, sizeof(head));
        cin >> n;
        int ans = n * n;
        for (int i = 1; i < n; i++)
        {
            int x, y, z;
            cin >> x >> y >> z;
            add(x, y, z);
            add(y, x, z);
        }
        mx[0] = n;
        root = 0;
        find(1, 0);
        divid(root);
        //cout << res<<" "<<ans << endl;
        res += n;
        int g = gcd(res, ans);
        cout << res / g << "/" << ans / g;
        return 0;
    
    }
    
  • 相关阅读:
    iOS.TextKit.02.文字图片混合排版
    翻翻乐游戏源码
    Dribbble客户端应用源码
    安卓版谍报馆客户端应用源码
    多文件上传 iOS功能
    最新模仿ios版微信应用源码
    类似QQ的应用毗邻(Pilin)即时聊天源码
    很类似新版天天动听音乐播放器安卓应用源码
    高仿安卓跑酷游戏源码
    类似美丽说应用源码带有详细开发说明文档
  • 原文地址:https://www.cnblogs.com/ruanbaiQAQ/p/15243905.html
Copyright © 2011-2022 走看看