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

【10.30校内测试】【玄学数论?】【点分治】【费用流动态开点】_第1张图片

Solution

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

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

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

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

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

Code

#include
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;
}

【10.30校内测试】【玄学数论?】【点分治】【费用流动态开点】_第2张图片

Solution

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

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

Code

#include
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;
}

【10.30校内测试】【玄学数论?】【点分治】【费用流动态开点】_第3张图片

【10.30校内测试】【玄学数论?】【点分治】【费用流动态开点】_第4张图片

Solution

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

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

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

这真的是noip模拟赛???

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

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

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

Code

#include
#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\n", 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;
}

 

转载于:https://www.cnblogs.com/wans-caesar-02111007/p/9877935.html

你可能感兴趣的:(【10.30校内测试】【玄学数论?】【点分治】【费用流动态开点】)