• 第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛 题解


    题目链接

    A - Wasserstein Distance

    模拟。从左往右填充每一个,如果某一个格子不足,需要从右边离他最近的有盈余的格子里拿一些来填充;如果某一个格子有盈余,那么多余部分往右扔过去。

    /*******************************
    	Judge Result : AC 
     *******************************/
    
    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn = 1e5 + 10;
    const int INF = 0x7FFFFFFF;
    int T, n;
    long long a[maxn], b[maxn];
    
    int main() {
    #ifdef ZHOUZHENTAO
    	freopen("test.in", "r", stdin);
    #endif
    
    	scanf("%d", &T);
    	while(T --) {
    		scanf("%d", &n);
    		for(int i = 1; i <= n; i ++) {
    			scanf("%lld", &a[i]);
    		}
    		for(int i = 1; i <= n; i ++) {
    			scanf("%lld", &b[i]);
    			a[i] = a[i] - b[i];
    		}
    		long long ans = 0;
    		int p = 1;
    		for(int i = 1; i <= n; i ++) {
    			if(a[i] == 0) continue;
    			if(a[i] > 0) ans += a[i], a[i + 1] += a[i], a[i] = 0;
    			else {
    				while(1) {
    					while(a[p] <= 0) p ++;
    					if(a[i] + a[p] >= 0) {
    						ans = ans + (p - i) * (-a[i]);
    						a[p] += a[i];
    						a[i] = 0;
    						break;
    					} else {
    						a[i] = a[i] + a[p];
    						ans = ans + (p - i) * a[p];
    						a[p] = 0;
    					}
    				}
    			}
    		}
    		printf("%lld
    ", ans);
    	}
    
    	return 0;
    }
    

    B - 合约数

    由于是处理子树问题,所以可以将树转成 dfs 序,然后就变成了区间问题。然后就是询问区间上有几个合约数,莫队操作一下就可以了。

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <string>
    #include <vector>
    #include <algorithm>
    using namespace std;
    #define LL long long
     
    const int maxn = 2e5 + 10;
    vector<int> g[maxn];
    int pri[maxn];
     
    int T, n, root;
    int a[maxn], sz;
    int L[maxn], R[maxn];
    int h[maxn], nx[maxn], to[maxn], cnt;
    int val[maxn], pos[maxn];
    int b[maxn], f[maxn];
     
    struct point {
        int id, l, r;
    }s[maxn];
     
    bool cmp(const point& a, const point& b) {
        if(pos[a.l] == pos[b.l]) return a.r < b.r;
        return pos[a.l] < pos[b.l];
    }
     
    int prime(int x) {
        if(x == 1) return 0;
        for(int i = 2; i * i <= x; i ++) {
            if(x % i == 0) return 0;
        }
        return 1;
    }
     
    void init() {
        for(int i = 1; i <= 10000; i ++) {
          pri[i] = prime(i);
        }
        for(int i = 4; i <= 10000; i ++) {
            if(pri[i]) continue;
            for(int j = 4; j <= i; j ++) {
                if(i % j) continue;
                if(pri[j]) continue;
                g[i].push_back(j);
            }
        }
    }
     
    void dfs(int x, int fa) {
        sz ++;
        a[sz] = x;
        L[x] = sz;
        for(int i = h[x]; i != -1; i = nx[i]) {
            if(to[i] == fa) continue;
            dfs(to[i], x);
        }
        R[x] = sz;
    }
     
    void add(int x, int y) {
        to[cnt] = y;
        nx[cnt] = h[x];
        h[x] = cnt ++;
    }
     
    void Delete(int x) {
        b[val[a[x]]] --;
    }
     
    void Insert(int x) {
        b[val[a[x]]] ++;
    }
     
    int main() {
        init();
     
        scanf("%d", &T);
        while(T --) {
            scanf("%d %d", &n, &root);
     
            int sqr = (int)sqrt(1.0 * n);
            for(int i = 1; i <= n; i ++) {
                pos[i] = i / sqr;
            }
     
            for(int i = 1; i <= 10000; i ++) {
                b[i] = 0;
            }
     
            cnt = 0;
            for(int i = 1; i <= n;i ++) {
                h[i] = -1;
            }
            for(int i = 0; i < n - 1; i ++) {
                int x, y;
                scanf("%d %d", &x, &y);
                add(x, y);
                add(y, x);
            }
            sz = 0;
            dfs(root, -1);
             
            /*
            for(int i = 1; i <= n; i ++) {
                printf("%d ", a[i]);
            }
            printf("
    ");
     
            for(int i = 1; i <= n; i ++) {
                printf("!!! %d %d %d
    ", i, L[i], R[i]);
            }
     
            printf("ok
    ");
            */
             
     
            for(int i = 1; i <= n; i ++) {
                scanf("%d", &val[i]);
            }
     
            for(int i = 1; i <= n; i ++) {
                s[i].id = i;
                s[i].l = L[i];
                s[i].r = R[i];
            }
     
            sort(s + 1, s + 1 + n, cmp);
            /*
            for(int i = 1; i <= n; i ++) {
                printf("q : %d %d %d
    ", s[i].id, s[i].l, s[i].r);
            }
            */
     
            for(int i = s[1].l; i <= s[1].r; i ++) {
                Insert(i);
            }
     
            f[s[1].id] = 0;
            for(int j = 0; j < g[val[s[1].id]].size(); j ++) {
                f[s[1].id] = f[s[1].id] + b[g[val[s[1].id]][j]];
            }
     
            int left = s[1].l, right = s[1].r;
            for(int i = 2; i <= n; i ++) {
                while(left > s[i].l) left --, Insert(left);
                while(right < s[i].r) right ++, Insert(right);
                while(left < s[i].l) Delete(left), left ++;
                while(right > s[i].r) Delete(right), right --;
                f[s[i].id] = 0;
                for(int j = 0; j < g[val[s[i].id]].size(); j ++) {
                    f[s[i].id] = f[s[i].id] + b[g[val[s[i].id]][j]];
                }
            }
     
            /*
            for(int i = 1; i <= n; i ++) {
                printf("!!! %d : %d
    ", i, f[i]);
            }
            */
     
            long long mod = 1e9 + 7;
            long long ans = 0;
            for(int i = 1; i <= n; i ++) {
                long long tmp = 1LL * i * f[i] % mod;
                ans = (ans + tmp) % mod;
            }
            printf("%lld
    ", ans);
     
        }
        return 0;
    }
     
    /*
    100
    13 10
    10 2
    3 1
    4 11
    4 12
    4 13
    10 4
    2 5
    10 3
    3 8
    3 9
    2 6
    2 7
    30 60 50 24 5 10 12 25 10 120 2 3 4
     
    */
    

    C - 序列变换

    枚举全排列,计算每一种排列需要的操作次数。从一个排列转换成另一个排列,可以模拟搞。从一个数字转换成另一个数字,只能选择一种方式,因此很简单。

    /*******************************
        Judge Result : AC
     *******************************/
     
    #include <bits/stdc++.h>
    using namespace std;
     
    const int maxn = 1e5 + 10;
    const int INF = 0x7FFFFFFF;
     
    int T, n;
     
    int a[maxn], b[maxn];
    int cost[20][20];
    int p[maxn];
    int u[maxn];
    int f[maxn];
     
    int work(int x, int y) {
        if(x == y) return 0;
        if(x < y) swap(x, y);
        int sum = 0;
        while(1) {
            if(x % 2 == 0) {
                if(x / 2 >= y) sum ++, x = x / 2;
                else return sum + x - y;
            } else {
                if(x == y) return sum;
                else sum ++,  x --;
            }
        }
        return sum;
    }
     
    int get() {
        int sum = 0;
        for(int i = 1; i <= n; i ++) {
            u[i] = p[i];
        }
        for(int i = 1; i <= n; i ++) {
            if(u[i] == i) continue;
            sum ++;
            for(int j = i + 1; j <= n; j ++) {
                if(u[j] == i) {
                    swap(u[i], u[j]);
                    break;
                }
            }
        }
        return sum;
    }
     
    int main() {
    #ifdef ZHOUZHENTAO
        freopen("test.in", "r", stdin);
    #endif
     
        scanf("%d", &T);
        while(T --) {
            scanf("%d", &n);
            for(int i = 1; i <= n; i ++) {
                scanf("%d", &a[i]);
            }
            for(int i = 1; i <= n; i ++) {
                scanf("%d", &b[i]);
            }
     
            for(int i = 1; i <= n; i ++) {
                for(int j = 1; j <= n; j ++) {
                    cost[i][j] = work(a[i], b[j]);
                    //cout << a[i] << " -> " << b[j] << " : " << cost[i][j] << endl;
                }
            }
     
            for(int i = 1; i <= n; i ++) {
                p[i] = i;
            }
     
            int ans = 2e9;
            do {
                int tmp = get();
                for(int i = 1; i <= n; i ++) {
                    tmp = tmp + cost[p[i]][i];
                }
                ans = min(ans, tmp);
            } while(next_permutation(p + 1, p + 1 + n));
     
            printf("%d
    ", ans);
        }
     
        return 0;
    }
    

    D - 数字游戏

    大致思路为如果无论 $n_1$ 为多少,都有一个 $n_2$ 能够找到,使得结果是 mod 的倍数,则先手输;否则后手输。由于 mod 范围不大,所以可以枚举一下 $n_2$ 的位数,然后暴力枚举处理,只要枚举 mod 范围内即可,因为取模之后的数不会超过 mod。

    E - 小Y吃苹果

    答案是 2 的 $n$ 次方。

    #include <stdio.h>
     
    int main()
    {
        int n;
        scanf("%d", &n);
        printf("%d
    ", 1 << n);
        return 0;
    }
    

    F - 1 + 2 = 3?

    找找规律可以发现满足条件的数字是二进制上没有相邻的 1。因此可以二分答案,然后数位 dp 计算方案数。

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <queue>
    using namespace std;
    #define LL long long
     
    LL dp[110][2];
    LL a[110];
    LL n;
     
    LL dfs(int len,int sta,bool limit)
    {
        if(len<0)
            return 1;
        if(dp[len][sta]!=-1&&!limit)
            return dp[len][sta];
        int up=limit?a[len]:1;
        LL ans=0;
        for(int i=0; i<=up; i++)
        {
            if(sta&&i==1)
                continue;
            ans+=dfs(len-1,i==1,limit&&i==up);
        }
        return limit?ans:dp[len][sta]=ans;
    }
     
    LL solve(LL x)
    {
        memset(dp,-1,sizeof dp);
        int cnt=0;
        while(x>0)
        {
            a[cnt++]=x%2;
            x/=2;
        }
        return dfs(cnt-1,0,1);
    }
     
    int main() {
        int T;
        scanf("%d", &T);
        while(T--) {
            scanf("%lld", &n);
            LL l = 1, r = 1e18;
            LL mid, ans;
            while(l <= r)
            {
                mid = (l + r) / 2;
                LL t = solve(mid) - 1;
                if(t < n)
                {
                    l = mid + 1;
                }
                else
                {
                    ans = mid;
                    r = mid - 1;
                }
            }
            printf("%lld
    ", ans);
        }
        return 0;
    }
    

    G - 小Y做比赛

    暂时不会做

    H - 小Y与多米诺骨牌

    先处理出选择 $i$ 位置往左倒,最左会使得 $L_i$ 也到下;往右倒,最右会使得 $R_i$ 也倒下。

    然后进行 dp,$dp_i$ 表示 $[1, i]$ 都倒下需要的最少操作次数。有两种途径,一种是 $[1, j]$ 先倒下,然后选择 $i$ 往左倒,使得 $[j+1,i]$ 都倒下;另一种是 $[1, j]$ 先倒下,然后选择 $j$ 向右倒,使得 $[j+1,i]$ 都倒下。两种都可以用线段树来维护来得到最优解。

    /*******************************
     Judge Result :
    *******************************/
     
    #include <bits/stdc++.h>
    using namespace std;
     
    const int maxn = 1e5 + 10;
    const int INF = 1e6;
    int x[maxn], y[maxn];
    int T, n;
    int L[maxn], R[maxn];
    int s[2][4 * maxn];
    int dp[maxn];
     
    vector<int> g[maxn];
     
    void build(int flag, int val, int l, int r, int rt) {
        s[flag][rt] = val;
        if(l == r) return ;
        int mid = (l + r) / 2;
        build(flag, val, l, mid, 2 * rt);
        build(flag, val, mid + 1, r, 2 * rt + 1);
    }
     
    void update(int flag, int op, int pos, int val, int l, int r, int rt) {
        if(l == r) {
            s[flag][rt] = val;
            return;
        }
        int mid = (l + r) / 2;
        if(pos <= mid) update(flag, op, pos, val, l, mid, 2 * rt);
        else update(flag, op, pos, val, mid + 1, r, 2 * rt + 1);
        if(op == 0) s[flag][rt] = min(s[flag][2 * rt], s[flag][2 * rt + 1]);
        else s[flag][rt] = max(s[flag][2 * rt], s[flag][2 * rt + 1]);
    }
     
    int getmin(int flag, int L, int R, int l, int r, int rt) {
        if(L <= l && r <= R) {
            return s[flag][rt];
        }
        int mid = (l + r) / 2;
        int left = INF, right = INF;
        if(L <= mid) left = getmin(flag, L, R, l, mid, 2 * rt);
        if(R > mid) right = getmin(flag, L, R, mid + 1, r, 2 * rt + 1);
        return min(left, right);
    }
     
    int getmax(int flag, int L, int R, int l, int r, int rt) {
        if(L <= l && r <= R) {
            return s[flag][rt];
        }
        int mid = (l + r) / 2;
        int left = 0, right = 0;
        if(L <= mid) left = getmax(flag, L, R, l, mid, 2 * rt);
        if(R > mid) right = getmax(flag, L, R, mid + 1, r, 2 * rt + 1);
        return max(left, right);
    }
     
    int main() {
    #ifdef ZHOUZHENTAO
      freopen("test.in", "r", stdin);
    #endif
     
        scanf("%d", &T);
        while(T --) {
            scanf("%d", &n);
            for(int i = 1; i <= n; i ++) {
                scanf("%d%d", &x[i], &y[i]);
            }
     
            build(0, INF, 1, n, 1);
            L[1] = 1;
            update(0, 0, 1, 1, 1, n, 1);
            for(int i = 2; i <= n; i ++) {
                update(0, 0, i, i, 1, n, 1);
                int left = 1, right = i, ans = -1;
                while(left <= right) {
                    int mid = (left + right) / 2;
                    if(x[mid] > x[i] - y[i]) ans = mid, right = mid - 1;
                    else left = mid + 1;
                }
                L[i] = getmin(0, ans, i, 1, n, 1);
                update(0, 0, i, L[i], 1, n, 1);
            }
     
            build(0, 0, 1, n, 1);
            R[n] = n;
            update(0, 1, n, n, 1, n, 1);
            for(int i = n - 1; i >= 1; i --) {
                update(0, 1, i, i, 1, n, 1);
                int left = i, right = n, ans = -1;
                while(left <= right) {
                    int mid = (left + right) / 2;
                    if(x[mid] < x[i] + y[i]) ans = mid, left = mid + 1;
                    else right = mid - 1;
                }
                R[i] = getmax(0, i, ans, 1, n, 1);
                update(0, 1, i, R[i], 1, n, 1);
            }
     
        /* 
            for(int i = 1; i <= n; i ++) {
                cout << i << " " << L[i] << " " << R[i] << endl;
            }
            */
     
            for(int i = 1; i <= n; i ++) {
                g[i].clear();
            }
            for(int i = 1; i <= n; i ++) {
                g[R[i]].push_back(i);
            }
     
            build(0, INF, 0, n, 1);
            build(1, INF, 0, n, 1);
     
            update(0, 0, 0, 0, 0, n, 1);
            update(1, 0, 0, 0, 0, n, 1);
            for(int i = 1; i <= n; i ++) {
                dp[i] = getmin(1, L[i] - 1, i - 1, 0, n, 1) + 1;
                dp[i] = min(dp[i], getmin(0, 0, i - 1, 0, n, 1) + 1);
                update(1, 0, i, dp[i], 0, n, 1);
              update(0, 0, i, dp[i], 0, n, 1);
                for(int j = 0; j < g[i].size(); j ++) {
                    update(0, 0, g[i][j] - 1, INF, 0, n, 1);
                }
            }
    /*
            for(int i = 1; i <= n ; i ++) {
                printf("dp[%d] = %d
    ", i, dp[i]);
            }
    */
            printf("%d
    ", dp[n]);
        }
     
        return 0;
    }
    

    I - 二数

    构造一下最大的小于等于 $n$ 的二数,以及最小的大于等于 $n$ 的二数,取小的就是答案。

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <string>
    using namespace std;
    #define LL long long
     
    char s[1000111];
     
    int main() {
        int T;
        scanf("%d", &T);
        while (~scanf("%s", &s)) {
            int k=strlen(s);
            if(s[0]=='1'&&k==1){
            printf("0
    ");
            continue;
            }
            int flag=0;
            for(int i=0;i<k;i++){
                if((s[i]-'0')%2==1){
                    if(s[i]=='9'){
                    flag=-1;
                    break;
                    }
                    for(int j=i+1;j<k;j++){
                        if(s[j]>'4'){
                        flag=1;
                        break;
                        }
                        else if(s[j]<'4'){
                        flag=-1;
                        break;
                        }
                    }
                    if(flag!=0) break;
                    flag=-1;
                    break;
                }
            }
     
     
            if(flag==0){
                printf("%s",s);
            }
            else if(flag==-1){
                int i=0;
                for(i=0;i<k;i++){
                    if((s[i]-'0')%2==1){
                        if(s[i]>'1'||i>0)
                        printf("%c",s[i]-1);
                        break;
                    }
                    printf("%c",s[i]);
                }
                i++;
                for(;i<k;i++){
                    printf("%c",'8');
                }
            }
            else if(flag==1){
                int i=0;
                for(i=0;i<k;i++){
                     
                    if((s[i]-'0')%2==1){
                        printf("%c",s[i]+1);
                        break;
                    }
                    printf("%c",s[i]);
                }
                i++;
                for(;i<k;i++){
                    printf("%c",'0');
                }
            }
            printf("
    ");
        }
        return 0;
    }
    

    J - 小Y写文章

    二分答案 $x$,需要验证答案小于等于 $x$ 能否做到。验证:可以考虑为有 $n + 1$ 个空位,需要 $m$ 个物品去填充。如果某个物品可以放在某个位置,就连边,容量为 1。需要额外增加一个节点,如果某个位置可以不放物品,这个节点就和那个位置连边。看看网络最大流是不是为 $n+1$ 即可。如果是,则说明存在放置的方案。

    /*******************************
        Judge Result : AC
     *******************************/
     
    #include <bits/stdc++.h>
    using namespace std;
     
    const int maxn = 500 + 10;
    const int INF = 0x7FFFFFFF;
    struct Edge
    {
        int from, to, cap, flow;
        Edge(int u, int v, int c, int f) :from(u), to(v), cap(c), flow(f){}
    };
    vector<Edge>edges;
    vector<int>G[maxn];
    bool vis[maxn];
    int d[maxn];
    int cur[maxn];
    int S, T;
     
    void init()
    {
        for (int i = 0; i < maxn; i++)
            G[i].clear();
        edges.clear();
    }
    void AddEdge(int from, int to, int cap)
    {
        //  cout << from << " -> " << to << "    " << cap << endl;
        edges.push_back(Edge(from, to, cap, 0));
        edges.push_back(Edge(to, from, 0, 0));
        int w = edges.size();
        G[from].push_back(w - 2);
        G[to].push_back(w - 1);
    }
    bool BFS()
    {
        memset(vis, 0, sizeof(vis));
        queue<int>Q;
        Q.push(S);
        d[S] = 0;
        vis[S] = 1;
        while (!Q.empty())
        {
            int x = Q.front();
            Q.pop();
            for (int i = 0; i<G[x].size(); i++)
            {
                Edge e = edges[G[x][i]];
                if (!vis[e.to] && e.cap>e.flow)
                {
                    vis[e.to] = 1;
                    d[e.to] = d[x] + 1;
                    Q.push(e.to);
                }
            }
        }
        return vis[T];
    }
    int DFS(int x, int a)
    {
        if (x == T || a == 0)
            return a;
        int flow = 0, f;
        for (int &i = cur[x]; i<G[x].size(); i++)
        {
            Edge e = edges[G[x][i]];
            if (d[x]+1 == d[e.to]&&(f=DFS(e.to,min(a,e.cap-e.flow)))>0)
            {
                edges[G[x][i]].flow+=f;
                edges[G[x][i] ^ 1].flow-=f;
                flow+=f;
                a-=f;
                if(a==0) break;
            }
        }
        if(!flow) d[x] = -1;
        return flow;
    }
    int dinic(int s, int t)
    {
        int flow = 0;
        while (BFS())
        {
            memset(cur, 0, sizeof(cur));
            flow += DFS(s, INF);
        }
        return flow;
    }
     
    int a[maxn], b[maxn];
    int n, m;
     
    int check(int limit) {
        init();
        int s = 0;
        int t = n + m + 2;
     
        S = n + m + 3;
        T = n + m + 4;
     
        AddEdge(S, s, INF);
        AddEdge(t, T, INF);
     
        AddEdge(n + m + 5, 0 + 1 + m, 1);
        AddEdge(n + m + 5, n + 1 + m, 1);
     
        for(int i = 1; i < n ; i ++) {
            if(abs(a[i] - a[i + 1]) <= limit) {
                AddEdge(n + m + 5, i + 1 + m, 1);
            }
        }
        AddEdge(s, n + m + 5, n + 1 - m);
        for(int i = 1; i <= m; i ++) {
            AddEdge(s, i, 1);
        }
        for(int i = 1; i <= m; i ++) {
            for(int j = 0; j <= n; j ++) {
                if(j == 0) {
                    if(abs(b[i] - a[1]) <= limit) {
                        AddEdge(i, j + 1 + m, 1);
                    }
                } else if(j < n) {
                    if(abs(b[i] - a[j]) <= limit && abs(b[i] - a[j + 1]) <= limit) {
                        //      cout << i << "  " << j << endl;
                        AddEdge(i, j + 1 + m, 1);
                    }
                } else {
                    if(abs(b[i] - a[n]) <= limit) {
                        AddEdge(i, j + 1 + m, 1);
                    }
                }
            }
        }
        for(int j = 0; j <= n; j ++) {
            AddEdge(j + 1 + m, t, 1);
        }
        if(dinic(S, T) == n + 1) return 1;
        return 0;
    }
     
    int main() {
    #ifdef ZHOUZHENTAO
        freopen("test.in", "r", stdin);
    #endif
        int T;
        scanf("%d", &T);
        while(T --) {
            scanf("%d%d", &n, &m);
            for(int i = 1; i <= n; i ++) {
                scanf("%d", &a[i]);
            }
            for(int i = 1; i <= m; i ++) {
                scanf("%d", &b[i]);
            }
            int left = 0, right = 2e9, ans;
            while(left <= right) {
                int mid = (left + right) / 2;
                if(check(mid)) ans = mid, right = mid - 1;
                else left = mid + 1;
            }
            printf("%d
    ", ans);
        }
        return 0;
    }
    

    K - 树上最大值

    有个东西叫线性基:支持插入元素,合并两个集合,计算集合中选一些数异或值最大的方法。所有操作复杂度均为 $O(log(value))$。有了这个方法这题就很简单了。

    枚举所有子树,A 集合在这个子树中,B 集合在子树以外部分。因此可以转化为区间问题,子树内用树形dp加上上面那个方法做就好了,子树外就是两个区间的并集,用前缀和后缀的并集即可。

    /*******************************
        Judge Result : AC
     *******************************/
     
    #include <bits/stdc++.h>
    using namespace std;
     
    const int maxn = 1e5 + 2;
    const int INF = 0x7FFFFFFF;
     
    struct Base{ 
        int a[30];
        void init(){for(int i=0;i<30;i++)a[i]=0;}
        void up(int &a, int b) {if(b>a)a=b;}
        void ins(int x){for(int i=29;~i;i--)if(x>>i&1){if(a[i])x^=a[i];else{a[i]=x;break;}}}
        int ask(){
            int t=0;
            for(int i=29;~i;i--)up(t,t^a[i]);
            return t;
        }
    };
     
    Base merge(const Base& b1, const Base& b2) {
        Base res = b1;
        for(int i = 0; i < 30; i ++) {
            if(b2.a[i]) res.ins(b2.a[i]);
        }
        return res;
    }
     
    int A[maxn], sz;
    int L[maxn], R[maxn];
    int h[maxn], nx[2 * maxn], to[2 * maxn], cnt;
    int n, val[maxn];
    Base node[maxn], pre[maxn], suf[maxn];
     
    void dfs(int x, int fa) {
        sz ++;
        A[sz] = x;
        L[x] = sz;
        node[x].init();
        node[x].ins(val[x]);
        for(int i = h[x]; i != -1; i = nx[i]) {
            if(to[i] == fa) continue;
            dfs(to[i], x);
            node[x] = merge(node[x], node[to[i]]);
        }
        R[x] = sz;
    }
     
    void add(int x, int y) {
        to[cnt] = y;
        nx[cnt] = h[x];
        h[x] = cnt ++;
    }
     
    int main() {
    #ifdef ZHOUZHENTAO
        freopen("test.in", "r", stdin);
    #endif
     
        scanf("%d", &n);
        for(int i = 1; i <= n; i ++) {
            h[i] = -1;
            scanf("%d", &val[i]);
        }
        for(int i = 0; i < n - 1; i ++) {
            int x, y;
            scanf("%d %d", &x, &y);
            add(x, y);
            add(y, x);
        }
        dfs(1, -1);
        pre[0].init();
        for(int i = 1; i <= n; i ++) {
            pre[i] = pre[i - 1];
            pre[i].ins(val[A[i]]);
        }
        suf[n + 1].init();
        for(int i = n; i >= 1; i --) {
            suf[i] = suf[i + 1];
            suf[i].ins(val[A[i]]);
        }
        int ans = 0;
        for(int i = 1; i <= n; i ++) {
            Base tmp = node[i];
            Base left, right;
            left.init();
            right.init();
            if(L[i] != 1) left = pre[L[i] - 1];
            if(R[i] != n) right = suf[R[i] + 1];
            ans = max(ans, tmp.ask() + merge(left, right).ask());
        }
        printf("%d
    ", ans);
        return 0;
    }
    

    L - K序列

    $dp_{i,j}$ 表示前 $i$ 个数,和对 $mod$ 取模的结果为 $j$ 的子序列的最长长度。开个滚动数组就好了。

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <string>
    using namespace std;
    #define LL long long
     
    int num[100111];
    int dp[2][10001111];
    int n, k;
     
    int main() {
      while (~scanf("%d %d", &n, &k)) {
        for (int i = 0; i < n; ++ i) {
          scanf("%d", num + i);
          num[i] %= k;
        }
        int wei = 0;
        memset(dp, -1, sizeof dp);
        dp[wei][0] = 0;
        for (int i = 0; i < n; ++ i) {
            for (int j = 0; j < k; ++ j) {
              dp[wei ^ 1][(j + num[i]) % k] = dp[wei][(j + num[i]) % k];
              if (dp[wei][j] != -1)
                dp[wei ^ 1][(j + num[i]) % k] = max(dp[wei][(j + num[i]) % k], dp[wei][j] + 1);
            }
            wei ^= 1;
        }
        printf("%d
    ", dp[wei][0]);
      }
      return 0;
    }
    
  • 相关阅读:
    UVA11174村民排队问题
    UVA11174村民排队问题
    UVA11137(立方数之和)
    UVA11137(立方数之和)
    UVA11134传说中的车(放棋子)
    UVA11134传说中的车(放棋子)
    UVA11100旅行(大包装小包,问最少多少个包)
    UVA11100旅行(大包装小包,问最少多少个包)
    UVA11078开放式学分制(前面-后面的最大值)
    UVA11078开放式学分制(前面-后面的最大值)
  • 原文地址:https://www.cnblogs.com/zufezzt/p/8858509.html
Copyright © 2020-2023  润新知