zoukankan      html  css  js  c++  java
  • 8.11考试差点爆0祭

    T1

    暴力50分:

    跑k次没有写错的dij

    看起来似乎是个树

    也有可能是森林

    也许我们可以把它当树做

    据Yousiki说是个入门的树形dp,but我不会

    正解:

    我们枚举两个距离最近的点的编号

    既然两个的int表示不同,就说明它们的二进制表示上至少有一位不同。

    假设枚举到第i位,就把这一位为0的点设置为源点,这一位为1的点设置成汇点

    然后跑多源多汇最短路

    就是设置一个超级源点s,一个超级汇点t

    s向每个源点连边权为0的边,所有汇点向t连边权为0的边,然后跑s到t的最短路

    stdのcode

    #include <queue>
    #include <cstdio>
    #include <cstring>
    
    template <class cls>
    inline cls min(const cls & a, const cls & b) {
        return a < b ? a : b;
    }
    
    const int mxn = 100005;
    const int mxm = 500005;
    const int inf = 0x3f3f3f3f;
    
    int n, m, k;
    
    int points[mxn];
    
    int tot;
    int hd[mxn];
    int nt[mxm];
    int to[mxm];
    int vl[mxm];
    
    inline void add_edge(int u, int v, int w) {
        nt[++tot] = hd[u];
        to[tot] = v;
        vl[tot] = w;
        hd[u] = tot;
    }
    
    int dis[mxn];
    
    struct data {
        int u, d;
    
        data(int _u, int _d) :
            u(_u), d(_d) {}
        
        bool operator < (const data & that) const {
            return d > that.d;
        }
    };
    
    std::priority_queue<data> heap;
    
    int main() {
        int cas;
        scanf("%d", &cas);
        for (int c = 0; c < cas; ++c) {
            scanf("%d%d%d", &n, &m, &k);
            memset(hd, 0, sizeof(int) * (n + 5)); tot = 0;
            for (int i = 0, u, v, w; i < m; ++i) {
                scanf("%d%d%d", &u, &v, &w);
                add_edge(u, v, w);
                add_edge(v, u, w);
            }
            for (int i = 0; i < k; ++i)
                scanf("%d", points + i);//神仙指针 
            int ans = inf;
            for (int i = 1; i < k; i <<= 1) {
                memset(dis, inf, sizeof(int) * (n + 5));//只memset dis数组的前n+5位 
                for (int j = 0, p; j < k; ++j)//这里对每个地下党按照输入顺序进行编号,然后枚举编号 
                    if (p = points[j], (j & i) == 0)//看j的第i位是否是0 
                        heap.push(data(p, dis[p] = 0));//把第i位为0的设置成了源点 
                while (!heap.empty()) {
                    int u = heap.top().u;
                    int d = heap.top().d;
                    heap.pop();
                    if (dis[u] != d)
                        continue;
                    for (int e = hd[u], v, w; e; e = nt[e])
                        if (v = to[e], w = vl[e], dis[v] > d + w)
                            heap.push(data(v, dis[v] = d + w));
                }
                for (int j = 0, p; j < k; ++j)
                    if (p = points[j], (j & i) != 0)
                        ans = min(ans, dis[p]);
            }
            printf("%d
    ", ans == inf ? -1 : ans);
        }
        return 0;
    }
    神奇的代码

    T2:

    处理到达的最大的权值(bfs)(不是dfs)

    tarjan缩一波点,来个DAG上dp

    stdのcode

    #include <cstdio>
    #include <cstring>
    
    template <class cls>
    inline cls min(const cls & a, const cls & b) {
        return a < b ? a : b;
    }
    
    template <class cls>
    inline cls max(const cls & a, const cls & b) {
        return a > b ? a : b;
    }
    
    const int mxn = 200005;
    const int mxm = 400005;
    
    int n, m, k, w[mxn];
    
    struct edge {
        int u, v;
    } edges[mxm];
    
    int tot;
    int hd[mxn];
    int to[mxm << 1];
    int nt[mxm << 1];
    
    inline void add_edge(int u, int v) {
        nt[++tot] = hd[u];
        to[tot] = v;
        hd[u] = tot;
    }
    
    int tim;
    int cnt;
    int top;
    int dfn[mxn];
    int low[mxn];
    int stk[mxn];
    int scc[mxn];
    
    void tarjan(int u) {
        dfn[u] = low[u] = ++tim; stk[++top] = u;
        for (int e = hd[u], v; e; e = nt[e])
            if (v = to[e], scc[v] == 0) {
                if (dfn[v] == 0)tarjan(v),
                    low[u] = min(low[u], low[v]);
                else
                    low[u] = min(low[u], dfn[v]);
            }
        if (dfn[u] == low[u]) {
            cnt += 1;
            do {
                scc[stk[top]] = cnt;
            } while (stk[top--] != u);
        }
    }
    
    int oe[mxn];
    int mx[mxn];
    
    int que[mxn];
    
    void bfs() {//O(n)预处理答案数组是真的强 
        int l = 0, r = 0;
        for (int i = 1; i <= cnt; ++i)
            if (oe[i] == 0)
                que[r++] = i;
        while (l < r) {
            int u = que[l++];
            for (int e = hd[u], v; e; e = nt[e])
                if (v = to[e], mx[v] = max(mx[v], mx[u]), --oe[v] == 0)
                    que[r++] = v;
        }
    }
    
    int main() {
        int cas;
        scanf("%d", &cas);
        for (int c = 0; c < cas; ++c) {
            scanf("%d%d%d", &n, &m, &k);
            for (int i = 1; i <= n; ++i)
                scanf("%d", w + i);
            memset(hd, 0, sizeof(int) * (n + 5)); tot = 0;
            for (int i = 0; i < m; ++i) {
                scanf("%d%d", &edges[i].u, &edges[i].v);
                add_edge(edges[i].u, edges[i].v);
            }
            tim = cnt = top = 0;
            memset(scc, 0, sizeof(int) * (n + 5));
            memset(dfn, 0, sizeof(int) * (n + 5));
            for (int i = 1; i <= n; ++i)//先搞一波tarjan 
                if (scc[i] == 0)
                    tarjan(i);
            memset(hd, 0, sizeof(int) * (cnt + 5)); tot = 0;
            memset(oe, 0, sizeof(int) * (cnt + 5));//这样memset省时间 
            memset(mx, 0, sizeof(int) * (cnt + 5));
            for (int i = 0; i < m; ++i) {
                int u = scc[edges[i].u];
                int v = scc[edges[i].v];
                if (u != v) 
                    add_edge(v, ua), oe[u] += 1;
            }
            for (int i = 1; i <= n; ++i)
                mx[scc[i]] = max(mx[scc[i]], w[i]);
            bfs();
            for (int i = 0, u, x; i < k; ++i) {
                scanf("%d%d", &u, &x);
                printf("%lld
    ", 1LL * x * mx[scc[u]]);//1LL表示转化成long long类型 
            }
        }
        return 0;
    }
    .....

    T3

    暴力:

    既然有20%的数据是颜色种类不超过5,所以我们可以开5棵线段树

    然鹅丧(sang)病(bing)的我开了100棵线段树

    真好

    100棵线段树的建树优化

    不是for(1~100)

    而是for c[l]到c[r]

    这样复杂度就是O(nlogn)的了

    正解:

    莫得强制在线

    把所有操作都读入进来

    修改:从颜色1的集合中删除,从颜色2的集合中加入

    所以按照颜色顺序,时间顺序处理每个操作

    这样就只需要一棵线段树了

    不用树链剖分:

    考虑树上的某个节点的权值加x,并且要计算一个点到根的路径上的点权之和

    当u+x之后,u的子树里的点到根的路径上的点权之和都+x。

    如果不用树链剖分,就跑一遍普通的dfs序。

    普通的dfs序保证一个点的子树是这个点出现的第一次和第三次之间的区间

    我们依旧用线段树来维护区间和。

    当点u的权值+x之后,在线段树上,整个区间的答案都+x

    求u到v路径上的点权之和:u到根+v到根的-2*lca到根的+lca的

     stdの神仙code

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    inline int getint()
    {
        int r = 0, c = getchar();
        
        for (; c < 48; c = getchar());
        for (; c > 47; c = getchar())
            r = r * 10 + c - 48;
        
        return r;
    }
    
    const int mxc = 100005;
    const int mxn = 100005;
    const int mxm = 200005;
    
    int n, m, c;
    
    int tt;
    int hd[mxn];
    int to[mxm];
    int nt[mxm];
    
    inline void addedge(int x, int y)
    {
        nt[++tt] = hd[x], to[tt] = y, hd[x] = tt;
        nt[++tt] = hd[y], to[tt] = x, hd[y] = tt;
    }
    
    struct data
    {
        int k, x, y;//[x,y]是要搞事情的区间,k是修改标记/查询的时间(k==0:表示当前操作是修改操作) 
        
        data() {} ;
        data(int a, int b, int c)//构造函数(把k赋值为a,把x赋值为b,把y赋值为c) 
            : k(a), x(b), y(c) {} ;
    };
    
    int color[mxn];
    
    #include <vector>
    
    vector<data> vec[mxc];//开了100个vector,每个对应一种颜色 
    
    int tim;
    int dfn[mxn];
    int top[mxn];
    int fat[mxn];
    int dep[mxn];
    int son[mxn];
    int siz[mxn];
    
    void dfs1(int u, int f)
    {
        siz[u] = 1;
        son[u] = 0;
        fat[u] = f;
        dep[u] = dep[f] + 1;
        
        for (int i = hd[u], v; i; i = nt[i])
            if (v = to[i], v != f)
            {
                dfs1(v, u);
                siz[u] += siz[v];
                if (siz[v] > siz[son[u]])
                    son[u] = v;
            }
    }
    
    void dfs2(int u, int f)
    {
        dfn[u] = ++tim;
        
        if (son[f] == u)
            top[u] = top[f];
        else
            top[u] = u;
        
        if (son[u])
            dfs2(son[u], u);
        
        for (int i = hd[u], v; i; i = nt[i])
            if (v = to[i], v != f && v != son[u])
                dfs2(v, u);
    }
    //上面是一波树剖 
    int bit[mxn];
    
    inline void add(int p, int v)//树状数组维护,常数较小 
    {
        for (; p <= n; p += p & -p)
            bit[p] += v;
    }
    
    inline int ask(int l, int r)
    {
        int sum = 0; --l;
        
        for (; r; r -= r & -r)
            sum += bit[r];
        
        for (; l; l -= l & -l)
            sum -= bit[l];
        
        return sum;
    }
    
    int ans[mxn];
    
    signed main()
    {
        int cas = getint();
        
        while (cas--) 
        {
            n = getint();
            m = getint();
            
            for (int i = 1; i <= n; ++i)
                vec[color[i] = getint()].push_back(data(0, i, +1));
            
            c = 0;
            
            for (int i = 1; i <= n; ++i)
                c = max(c, color[i]);
    
            memset(hd, 0, sizeof(int) * (n + 5)); tt = 0;
            
            for (int i = 1; i < n; ++i)
            {
                int x = getint();
                int y = getint();
                
                addedge(x, y);
            }
            
            for (int i = 1; i <= m; ++i)
            {
                if (getint() == 1)
                {
                    int p = getint();
                    int a = color[p];
                    int b = color[p] = getint();
                    
                    vec[a].push_back(data(0, p, -1));
                    vec[b].push_back(data(0, p, +1));
                }
                else
                {
                    int x = getint();
                    int y = getint();
                    int k = getint();
                    
                    vec[k].push_back(data(i, x, y));
                }
            }
            
            dfs1(1, 0);
            dfs2(1, 0);
            
            memset(ans, -1, sizeof ans);
            
            for (int k = 1; k <= c; ++k)//分颜色来搞 
            {
                int sz = vec[k].size();
                
                memset(bit, 0, sizeof bit);
                
                for (int i = 0; i < sz; ++i)
                {
                    const data &d = vec[k][i];
                    
                    ans[d.k] = 0;
                    
                    if (d.k == 0)//进行修改操作 
                        add(dfn[d.x], d.y);
                    else
                    {
                        int a = d.x, ta = top[a];
                        int b = d.y, tb = top[b];
                        
                        while (ta != tb)
                        {
                            if (dep[ta] >= dep[tb])
                                ans[d.k] += ask(dfn[ta], dfn[a]), ta = top[a = fat[ta]];
                            else
                                ans[d.k] += ask(dfn[tb], dfn[b]), tb = top[b = fat[tb]];
                        }
                        
                        if (dep[a] <= dep[b])
                            ans[d.k] += ask(dfn[a], dfn[b]);
                        else
                            ans[d.k] += ask(dfn[b], dfn[a]);
                    }
                }
            }
            
            for (int i = 1; i <= m; ++i)
                if (ans[i] >= 0)
                    printf("%d
    ", ans[i]);
    
            for (int i = 1; i <= c; ++i)
                vec[i].clear();
            
            tim = 0;
        }
        
        return 0;
    }
    神仙Yousiki
  • 相关阅读:
    git修改远程仓库地址
    Result Maps collection already contains value for com.miniprogram.meirong.user.dao.UserMapper.BaseResultMap
    查看微信小程序的appID和secret
    Springboot配置fastjson开发
    本地仓库上传码云
    总结分析Java常见的四种引用
    equals和hashcode 和 ==方法说明
    关于Class.forName(className).newInstance()介绍
    Java枚举
    抽象工厂模式-与-工厂方法模式区别
  • 原文地址:https://www.cnblogs.com/lcez56jsy/p/11336705.html
Copyright © 2011-2022 走看看