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\n", 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\n", 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\n", ans[i]);

        for (int i = 1; i <= c; ++i)
            vec[i].clear();
        
        tim = 0;
    }
    
    return 0;
}
神仙Yousiki

 

posted @ 2019-08-13 15:45  千载煜  阅读(159)  评论(0编辑  收藏  举报