• 【10.30校内测试】【玄学数论?】【点分治】【费用流动态开点】


    Solution

    这种题怎么推??当然是打表啊!

    打表发现规律,满足上述条件的数对一定满足大数减小数等于它们的gcd??

    然而考试的时候知道了这个规律也没有写出来....

    知道了以上结论后,就枚举两数的差d,使大数为$kd$,小数为$kd-d$,它们的gcd一定就是d了,那么只用判断两数的异或是否也等于d即可。

    主要是复杂度证明了??根据迷之调和级数,这样做复杂度是$O(nln_n)$的

    Code

    #include<bits/stdc++.h>
    using namespace std;
    
    int main() {
        freopen("gcd.in", "r", stdin);
        freopen("gcd.out", "w", stdout);
        int n, ans = 0;
        scanf("%d", &n);
        for(int i = 1; i <= n; i ++) {
            for(int k = 2; k <= n / i; k ++) {
                if(((k * i) ^ (k - 1) * i) == i)    ans ++;
            }
        }
        printf("%d", ans);
        return 0;
    }

    Solution

    “树上路径的题很多都可以用点分治解决”

    所以这道题就是点分治叻,对于每个分治出的树的子树,按顺序遍历子树,更新答案后将子树新的贡献加入。这种贡献用set维护单增,用lower_bound查询即可。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    
    int n, s, e, k = 0x3f3f3f3f;
    
    inline int read() {
        int x = 0;    char ch = getchar(); int t = 0;
        while(!isdigit(ch))    t |= (ch == '-'), ch = getchar();
        while(isdigit(ch))    x = x * 10 + ch - '0', ch = getchar();
        return x *= t ? -1 : 1;
    }
    
    struct Node {
        int v, nex, w;
    } Edge[200005];
    
    int stot, h[100005];
    inline void add(int u, int v, int w) {
        Edge[++stot] = (Node) {v, h[u], w};
        h[u] = stot;
    }
    
    int siz[100005], rt, asiz, vis[100005], sum;
    inline void find_root(int u, int f) {
        siz[u] = 1;
        int tmp = 0;
        for(int i = h[u]; i; i = Edge[i].nex) {
            int v = Edge[i].v;
            if(vis[v] || v == f)    continue;
            find_root(v, u);
            siz[u] += siz[v];
            if(siz[v] > tmp)    tmp = siz[v];
        }
        if(sum - siz[u] > tmp)    tmp = sum - siz[u];
        if(tmp < asiz)    rt = u, asiz = tmp;
    }
    
    int dis[100005], dep[100005];
    inline void get_dis(int u, int f) {
        dep[++dep[0]] = dis[u];
        for(int i = h[u]; i; i = Edge[i].nex) {
            int v = Edge[i].v;
            if(vis[v] || v == f)    continue;
            dis[v] = dis[u] + Edge[i].w;
            get_dis(v, u);
        }
    }
    
    set < int > st;
    inline void cal(int u) {
        for(int i = h[u]; i; i = Edge[i].nex) {
            int v = Edge[i].v;
            if(vis[v])    continue;
            dep[0] = 0;    dis[v] = Edge[i].w;
            get_dis(v, u);
            
            for(int i = 1; i <= dep[0]; i ++) {
                if(dep[i] >= s && dep[i] <= e) {
                    k = min(k, dep[i]);    continue;
                }
                if(dep[i] > e)    continue;
                int tmp = s - dep[i];
                set < int > :: iterator it;
                it = st.lower_bound(tmp);
                if(*it + dep[i] > e || *it + dep[i] < s)    continue;
                k = min(k, *it + dep[i]);
            }
            for(int i = 1; i <= dep[0]; i ++) {
                if(dep[i] < s)
                    st.insert(dep[i]);
            }
        }
        st.clear();
    }
    
    
    inline void work(int u) {
        vis[u] = 1;
        cal(u);
        for(int i = h[u]; i; i = Edge[i].nex) {
            int v = Edge[i].v;
            if(vis[v])    continue;
            sum = siz[v], asiz = 0x3f3f3f3f;
            find_root(v, u);
            work(rt);
        }
    }
    
    int main() {
        freopen("path.in", "r", stdin);
        freopen("path.out", "w", stdout);
        n = read(), s = read(), e = read();
        for(int i = 1; i < n; i ++) {
            int u = read(), v = read(), w = read();
            add(u, v, w);    add(v, u, w);
        }
        sum = n, asiz = 0x3f3f3f3f;
        find_root(1, 0);
        work(rt);
        if(k > e)    printf("-1");
        else        printf("%d", k);
        return 0;
    }

    Solution

    乍一看好像之前做过的费用流经典模型修车啊??

    再一看数据范围,这么多个点是怎么回事???

    所以隐藏在省选题面具下实际上是一道NOI的题:美食节QAQ

    这真的是noip模拟赛???

    我们发现这道题如果要像修车那样全部建边,明显时间、空间都承受不起。而这两道题唯一的区别也在这里,这道题有很多很多边是无用的,所以考虑怎么把这些边的空间给省去。

    所以是动态开点了,先把所有洞拆点,但一开始只把所有n连到拆的第一个点(表示相对这个洞被倒数第一个进入),找到一条增广路后把选用的这个拆的点往后移一位,也就是这个洞被倒数第2,倒数第3....次选用所代表的点,然后此时再将这些点加边到图中去跑,也就是可能有用的点再连边,无用就不管。

    一开始把连向汇点的建边放到循环内部去了,建了好多,调了好久QAQ

    Code

    #include<bits/stdc++.h>
    #define oo 0x3f3f3f3f
    using namespace std;
    
    int n, m;
    
    inline int read() {
        int x = 0;    char ch = getchar(); int t = 0;
        while(!isdigit(ch))    t |= (ch == '-'), ch = getchar();
        while(isdigit(ch))    x = x * 10 + ch - '0', ch = getchar();
        return x *= t ? -1 : 1;
    }
    
    struct Node {
        int v, nex, f, w;
    } Edge[5000005];
    
    int h[100005], stot = 1;
    void add(int u, int v, int f, int w) {
        Edge[++stot] = (Node) {v, h[u], f, w};
        h[u] = stot;
        Edge[++stot] = (Node) {u, h[v], 0, -w};
        h[v] = stot;
    }
    
    int vis[100005], dis[100005], S, T, pree[100005], preu[100005];
    bool Spfa() {
        memset(vis, 0, sizeof(vis));
        memset(dis, 0x3f3f3f3f, sizeof(dis));
        dis[S] = 0;    vis[S] = 1;    queue < int > q; q.push(S);
        while(!q.empty()) {
            int u = q.front();    q.pop();    vis[u] = 0;
            for(int i = h[u]; i; i = Edge[i].nex) {
                int v = Edge[i].v;
                if(Edge[i].f && dis[v] > dis[u] + Edge[i].w) {
                    dis[v] = dis[u] + Edge[i].w;
                    pree[v] = i, preu[v] = u;
                    if(!vis[v])    vis[v] = 1, q.push(v);
                }
            }
        }
        return dis[T] < dis[T + 1];
    }
    
    long long mincost;
    void Doge() {
        int u = T, delta = oo;
        while(u != S) {
            delta = min(delta, Edge[pree[u]].f);
            u = preu[u];
        }
        u = T;
        while(u != S) {
            Edge[pree[u]].f -= delta;
            Edge[pree[u] ^ 1].f += delta;
            u = preu[u];
        }
        mincost += delta * dis[T];
    }
    
    int p[50], t[50][105], tot;
    int main() {
        freopen("bird.in", "r", stdin);
        freopen("bird.out", "w", stdout);
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i ++) {
            p[i] = read(), tot += p[i];
            add(S, i, p[i], 0);
        }
        S = 0, T = n + m * tot + 1;
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= m; j ++) {
                t[i][j] = read();
                add(i, n + (j - 1) * tot + 1, 1, t[i][j]);
            }
        for(int i = 1; i <= m; i ++)
            add(n + (i - 1) * tot + 1, T, 1, 0);
        while(Spfa()) {
            Doge();
            int x = preu[T] + 1;
            //printf("%d
    ", x);
            for(int i = 1; i <= n; i ++) {
                int pos;    int tmp = (x - n) % tot;    
                if(!tmp)    pos = (x - n) / tot, tmp = tot;    
                else        pos = (x - n) / tot + 1;
                add(i, x, 1, t[i][pos] * tmp);
            }
                
            add(x, T, 1, 0);
        }
        printf("%lld", mincost);
        return 0;
    }
  • 相关阅读:
    一些用位运算解决的方法
    代码之美中的二分搜索算法Java版
    Connection listeners: accepting TCP connections 翻译
    Android网络编程1
    HelloWorldForU 九度Online Judge
    Ubuntu下设置默认java
    分享一个libevent server——cliserver
    编程原则
    hive迁移hadoop namenode时遇到的问题
    python for语句
  • 原文地址:https://www.cnblogs.com/wans-caesar-02111007/p/9877935.html
Copyright © 2020-2023  润新知