zoukankan      html  css  js  c++  java
  • 暑假N天乐【比赛篇】 —— 2019杭电暑期多校训练营(第四场)

    本来想说这场放掉了,算了还是补了吧...

    以下题解包括:

    [1001【HDU-6614】 \ 1003【HDU-6616】 \ 1007【HDU-6620】 \ 1008【HDU-6621】 \ 1010【HDU-6623】 ]

    【1001】 思维 HDU-6614 AND Minimum Spanning Tree

    http://acm.hdu.edu.cn/showproblem.php?pid=6614

    需要建一颗树,使得边权值和最小,边权为两点的 “&” 值。

    假设二进制位 101,那就和 2 连接;假设为 111,那就看一下有没有 8 这个点,没有的话就和点 1 连接。

    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <bitset>
    #include <cctype>
    #include <cstdio>
    #include <vector>
    #include <string>
    #include <cstdlib>
    #include <cstring>
    #include <fstream>
    #include <iomanip>
    #include <numeric>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const double PI = acos(-1.0);
    const double eps = 1e-6;
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9 + 7;
    
    const int maxn = 2e5+5;
    
    int a[maxn];
    
    int wei(int x) {
        int ans = 0;
        while(x) {
            ans ++;
            x /= 2;
        }
        return ans;
    }
    
    int main() {
        int t;
        scanf("%d", &t);
        while(t--) {
            int n;
            scanf("%d", &n);
            int x = wei(n);
            int s = 0;
            for(int i = 0; i < x; i++) {
                if((n>>i) & 1) {
                    s++;
                }   
            }
    
            int ans = 0;
            if(s == x) {
                ans ++;
            }
            printf("%d
    ", ans);
            for(int i = 2; i <= n; i++) {
                if(ans == 1 && i == n) {    
                    printf("1
    ");
                    break;
                }
                for(int j = 0; j < x; j++) {
                    if((i>>j) & 1) {
                        continue;
                    }
                    printf("%d%c", (1<<j), i==n?'
    ':' ');
                    break;
                }
            } 
        }
        return 0;
    }
    

    【1003】 思维 HDU-6616 Divide the Stones

    http://acm.hdu.edu.cn/showproblem.php?pid=6616

    给定重量 (1)(n)(n) 个石头,让你分成重量相等,数量也相等的 (k) 组,保证 (k)(n) 的约数。输出具体的分配方案。

    参考:https://www.cnblogs.com/isakovsky/p/11281662.html

    首先,如果 (1)(n) 之和不能整除 (k),那么一定不能分配;否则一定能。

    (m=n/k)(m) 是每组分到的石头块数。我们把n块石头排成这样 (m*k) 的矩阵,假设 12 块石头,分成 3 组。

    [egin{bmatrix} 1 & 2 & 3 \ 4 & 5 & 6 \ 7 & 8 & 9 \ 10 &11 &12 end{bmatrix} ]

    • m为偶数,只要每次取头尾各 (m/2) 个即可。
    • m为奇数,将后两列之和构造成从上到下递增1,然后剩下的奇数个,一行构造成递增,一行构造成递减。
    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <bitset>
    #include <cctype>
    #include <cstdio>
    #include <vector>
    #include <string>
    #include <cstdlib>
    #include <cstring>
    #include <fstream>
    #include <iomanip>
    #include <numeric>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const double PI = acos(-1.0);
    const double eps = 1e-6;
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9 + 7;
    
    
    
    int main() {
        int t;
        scanf("%d", &t);
        while(t--) {
            int n, k;
            scanf("%d%d", &n, &k);
            ll sum = 1ll*n*(n+1)/2;
            if(k == 1) {
                printf("yes
    ");
                for(int i = 1; i <= n; i++) {
                    printf("%d%c", i, i==n?'
    ':' ');
                }
            }
            else if(sum % k) {
                printf("no
    ");
            }
            else {
                printf("yes
    ");
                if((n/k) % 2 == 0) {
                    int x = n / k;
                    int cnt = 0;
                    for(int i = 1; i <= n/2; i++) {
                        printf("%d %d", i, n-i+1);
                        cnt += 2;
                        if(cnt == x) {
                            printf("
    ");
                            cnt = 0;
                        }
                        else {
                            printf(" ");
                        }
                    }
                }
                else {
                    int x = n / k;
                    int tot = 1 + 2*k - k/2;
                    int temp = 1;
                    for(int i = 1; i <= k; i++) {
                        for(int j = 1; j <= x-2; j++) {
                            if(j % 2 == 1) {
                                printf("%d ", n-(j-1)*k-i+1);
                            }
                            else {
                                printf("%d ", n-j*k+1+i-1);
                            }
                        }
                        printf("%d %d
    ", temp, tot - temp);
                        tot ++;
                        temp += 2;
                        if(temp > k) {
                            temp = 2;
                        }
                    }
                }
            }
        }
        return 0;
    }
    

    【1007】 思维 HDU-6620 Just an Old Puzzle

    http://acm.hdu.edu.cn/showproblem.php?pid=6620

    给定一个数字拼图,问能否能复原成原来的样子(120步以内)。

    经典的华容道问题变形。

    定理:逆序数奇偶相同时,可以互相转化,逆序数奇偶不同,不能互相转化。

    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <bitset>
    #include <cctype>
    #include <cstdio>
    #include <vector>
    #include <string>
    #include <cstdlib>
    #include <cstring>
    #include <fstream>
    #include <iomanip>
    #include <numeric>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const double PI = acos(-1.0);
    const double eps = 1e-6;
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9 + 7;
    
    int a[20];
    
    int main( ) {
        int t;
        scanf("%d", &t);
        int flag, ans;
        while(t--) {
            ans = 0;
            for(int i = 1; i <= 16; i++) {
                scanf("%d", &a[i]);
                if(a[i] == 0) {
                    flag = i / 4 + (i % 4 != 0);
                }
                else {
                    for(int j = 1; j < i; j++) {
                        if(a[j] == 0) {
                            continue;
                        }
                        if(a[j] > a[i]) {
                            ans++;
                        } 
                    }
                }
            }
            printf((4-flag) % 2 == ans % 2 ? "Yes
    " : "No
    ");
        }
        return 0;
    }
    

    【1008】 主席树 HDU-6621 K-th Closest Distance

    http://acm.hdu.edu.cn/showproblem.php?pid=6621

    给定 (n) 个数,(q) 次查询,每次查询 ([l , r]) 内, (| a[i] - p |)(k) 大的数,且强制要求在线。其中 (n leq 1e^5 q leq 1e^5)

    比赛时候一直想着主席树,一直没办法吧 k 这个大常数优化掉,就 T 了一整场。结果发现可以换一种存法就行了,或许应该叫权值主席树?QAQ

    主席树的结点直接就是 (1e^6) 个值,不进行离散化,对每个值都单独统计出现的次数。二分查询 ((p-mid,p+mid)) 中数的个数。

    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <bitset>
    #include <cctype>
    #include <cstdio>
    #include <vector>
    #include <string>
    #include <cstdlib>
    #include <cstring>
    #include <fstream>
    #include <iomanip>
    #include <numeric>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const double PI = acos(-1.0);
    const double eps = 1e-6;
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9 + 7;
    
    const int maxn = 1e6 + 5;
    
    int n, m, cnt, new_n;
    int a[maxn];
    int root[maxn];
    vector<int> v;
    struct node {
        int l, r, sum;
    }T[maxn*40];
    
    void init() {
        v.clear();
        cnt = 0;
    }
    
    inline void update(int l, int r, int &x, int y, int val) {
        T[++cnt] = T[y];
        T[cnt].sum ++;
        x = cnt;
        if(l == r) {
            return ;
        }
        int mid = (l+r) / 2;
        if(mid >= val) {
            update(l, mid, T[x].l, T[y].l, val);
        }
        else {
            update(mid+1, r, T[x].r, T[y].r, val);
        }
    }
    
    inline int query(int L, int R, int l, int r, int x, int y) {
        if(L <= l && r <= R) {
            return T[y].sum - T[x].sum;
        }
        int mid = (l+r) / 2;
        int ans = 0;
        if(L <= mid) {
            ans += query(L, R, l, mid, T[x].l, T[y].l);
        }
        if(R > mid) {
            ans += query(L, R, mid+1, r, T[x].r, T[y].r);
        }
        return ans;
    }
    
    int main() {
        int t;
        scanf("%d", &t);
        while(t--) {
            init();
            int MAX = 1000000;
            scanf("%d%d", &n, &m);
            for(int i = 1; i <= n; i++) {
                scanf("%d", &a[i]);
            }
            for(int i = 1; i <= n; i++) {
                update(1, MAX, root[i], root[i-1], a[i]);
            }
            int ans = 0;
            for(int i = 1; i <= m; i++) {
                int l, r, p, k;
                scanf("%d%d%d%d", &l, &r, &p, &k);
                l = l^ans;
                r = r^ans;
                p = p^ans;
                k = k^ans;
                
                int L = 0;
                int R = MAX;
                while(L <= R) {
                    int mid = (L+R) >> 1;
                    if(query(max(1, p-mid), min(MAX, p+mid), 1, MAX, root[l-1], root[r]) >= k) {
                        ans = mid;
                        R = mid - 1;
                    }
                    else {
                        L = mid + 1;
                    }
                }
                printf("%d
    ", ans);
            }
        }
        return 0;
    }
    

    【1010】 数学 HDU-6623 Minimal Power of Prime

    http://acm.hdu.edu.cn/showproblem.php?pid=6623

    给定一个数 (n),求它的素因子里最大的指数是多少。

    考虑 (n^{1/5}) 范围内的素数,计算范围内的最小素数的次数是多少,然后除去这些素数,得到剩余的数字 (m),考虑 (m) 如果大于 10009(就是素数1~n^(1/5)范围内的素数,n的范围是(10009,1e18),此时已知最小素数时10009,所以最是最小素数的4次方,分别讨论m是否为素数的2,3,4次方即可,如果都不是,就是1次方)。要特判1。

    #include <map>
    #include <set>
    #include <list>
    #include <cmath>
    #include <ctime>
    #include <deque>
    #include <stack>
    #include <queue>
    #include <bitset>
    #include <cctype>
    #include <cstdio>
    #include <vector>
    #include <string>
    #include <cstdlib>
    #include <cstring>
    #include <fstream>
    #include <iomanip>
    #include <numeric>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const double PI = acos(-1.0);
    const double eps = 1e-6;
    const int inf = 0x3f3f3f3f;
    const int mod = 1e9 + 7;
    
    const int maxn = 1e4+5;
    
    int vis[maxn], pri[maxn];
    int tot;
    ll n;
    
    void prime() {
        for(int i = 2; i < maxn; i++) {
            if(vis[i] == 0) {
                pri[tot++] = i;
                for(int j = i*i; j < maxn; j+=i) {
                    vis[j] = 1;
                }
            }
        }
    }
    
    int check(ll x) {
        int l = 0;
        int r = 1000000;
        while(l <= r) {
            int mid = (l+r) >> 1;
            if(1ll*mid*mid*mid == x) {
                return 1;
            }
            if(1ll*mid*mid*mid > x) {
                r = mid-1;
            }
            else{
                l = mid+1;
            }
        }
        return 0;
    }
    
    int main() {
        prime();
        int t;
        scanf("%d", &t);
        while(t--) {
            scanf("%lld", &n);
            if(n == 1) {
                printf("0
    ");
                continue;
            }
            ll ans = n;
            for(int i = 0; i < tot; i++) {
                if(n == 1) {
                    break;
                }
                if(n % pri[i] == 0) {
                    int cnt = 0;
                    while(n % pri[i] == 0) {
                        cnt ++;
                        n /= pri[i];
                    }
                    ans = min(ans, 1ll*cnt);
                }
            }
            if(n >= maxn) {
                ll s1 = (ll)sqrt(n);
                ll s2 = (ll)sqrt(s1);
                if(s2*s2*s2*s2 == n) {
                    ans = min(ans, 4ll);
                }
                else if(s1*s1 == n) {
                    ans = min(ans, 2ll);
                }
                else if(check(n)) {
                    ans = min(ans, 3ll);
                }
                else {
                    ans = min(ans, 1ll);
                }
            }
            printf("%lld
    ", ans);
        }
        return 0;
    }
    
  • 相关阅读:
    前端资料
    贪心
    二叉树的最大深度
    最长回文子串
    动态规划-tsp
    动态规划
    spfa与SLF和LLL(复习)
    动态规划之最长 公共子序列和上升子序列
    最近最远距离之暴力优化
    基于Element-UI封装的季度插件
  • 原文地址:https://www.cnblogs.com/Decray/p/11305275.html
Copyright © 2011-2022 走看看