• 2016-2017 7th BSUIR Open Programming Contest. Semifinal


    B - Traveling Salesman Problem

    给一个分子是奇数,分母是2的幂次的真分数,要用两种操作把它从1构造出来。

    一种是除以2,另一种是求对1的补数。

    画了一下手模觉得每个位只能有一次机会被构造。(除非连续求几次补数这种**行为)。

    类似快速幂这样分解的时候要记得把0也弄上不然就直接死循环T了。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
     
    string trans(ll a) {
        string sa;
        while(a) {
            //cout<<"a="<<a<<endl;
            if(a & 1)
                sa = '1' + sa;
            else
                sa = '0' + sa;
            a >>= 1;
        }
        return sa;
    }
     
    int main() {
    #ifdef Yinku
        freopen("Yinku.in", "r", stdin);
    #endif // Yinku
        ll a, b;
        while(cin >> a >> b) {
            string sa, sb;
            sb += '1';
            ll cb = 1;
            while(b) {
                cb <<= 1;
                sb += '0';
                b--;
            }
            ll ca = a;
            while(a) {
                if(a & 1)
                    sa = '1' + sa;
                else
                    sa = '0' + sa;
                a >>= 1;
            }
            //cout << sa << "/" << sb << endl;
            //cout << ca << "/" << cb << endl << endl;
            string ans;
            while(sb.size() > 1) {
                if(ca != 1 && (sa.size() == sb.size() - 1)) {
                    ans = '1' + ans;
                    ca = cb - ca;
                    sa = trans(ca);
                } else {
                    cb >>= 1;
                    sb = sb.substr(0, sb.size() - 1);
                    ans = '0' + ans;
                }
                //cout << sa << "/" << sb << endl;
                //cout << ca << "/" << cb << endl << endl;
            }
            cout << ans << endl;
        }
    }
    

    C - Maya's message

    题意,给一串带问号的数字,要你把问号换成数字,把n构造成k的最小的倍数,并且n不能有前导零。

    这个超级坑爹,输入自己有前导零的,有毒。

    一开始想了很久不知道怎么搞,觉得看起来像以前的数位dp,只不过这个不是问[l,r]之间的数的数量,不需要考虑什么上界的限制,只需要确定余数。

    从最低位开始数位dp的话,不知道是怎么样才能构造最小,所以只能够从最高位开始构造,这样就蛮麻烦的。

    用类似bool数组的办法来记录每个余数种类的可行性。每次最高位的dp值保存两个信息,首先是它的上一步是谁(用来复原方案),然后就是它这一路选过的数字的大小在这一层里面的名次(用来贪心转移到下一层)。

    最后回答答案的时候,要注意是要减去每个位的值,因为存的余数是“从最高位开始的前缀和”,当然是要反过来减掉。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
     
    char s[1005];
    char dp[1005][10][1005];
     
    int qpow(int x, int n, int m) {
        int res = 1;
        while(n) {
            if(n & 1)
                res = res * x % m;
            x = x * x % m;
            n >>= 1;
        }
        return res;
    }
     
    int pow10[1005];
     
    typedef pair<int, pair<int, int> > pii;
     
    pii p[2][100005];
    int ptop[2];
     
    int ans[1005];
     
    int main() {
    #ifdef Yinku
        freopen("Yinku.in", "r", stdin);
    #endif // Yinku
        int n, k;
        while(~scanf("%d%d", &n, &k)) {
            scanf("%s", s + 1);
            if(n == 1) {
                if(s[1] == '?')
                    puts("0");
                else {
                    int x = s[1] - '0';
                    if(x % k == 0)
                        puts(s + 1);
                    else
                        puts("-1");
                }
                continue;
            }
     
            if(s[1] == '0') {
                puts("-1");
                continue;
            }
     
            pow10[0] = 1;
            for(int i = 1; i <= n; ++i) {
                pow10[i] = pow10[i - 1] * 10 % k;
            }
            memset(dp, -1, sizeof(dp));
     
            ptop[1] = 0;
            if(s[1] != '?') {
                int curj = s[1] - '0';
                int curk = pow10[n - 1] * curj % k;
                dp[1][curj][curk] = 10;
                p[1][++ptop[1]] = {10, make_pair(curj, curk)};
            } else {
                for(int curj = 1; curj <= 9; ++curj) {
                    int curk = pow10[n - 1] * curj % k;
                    dp[1][curj][curk] = 10;
                    p[1][++ptop[1]] = {10, make_pair(curj, curk)};
                }
            }
     
            for(int i = 2; i <= n; ++i) {
                int cntl = 0;
     
                register int pre = (i - 1) & 1, cur = (i & 1);
                ptop[cur] = 0;
     
                sort(p[pre] + 1, p[pre] + 1 + ptop[pre]);
     
                if(s[i] != '?') {
                    int curj = s[i] - '0';
                    for(int pj = 1; pj <= ptop[pre]; ++pj) {
                        int prej = p[pre][pj].second.first;
                        int prek = p[pre][pj].second.second;
                        int curk = (prek + pow10[n - i] * curj) % k;
                        if(dp[i][curj][curk] == -1) {
                            dp[i][curj][curk] = prej;
                            p[cur][++ptop[cur]] = {++cntl, make_pair(curj, curk)};
                        }
                    }
                } else {
                    for(int pj = 1; pj <= ptop[pre]; ++pj) {
                        for(int curj = 0; curj <= 9; ++curj) {
                            int prej = p[pre][pj].second.first;
                            int prek = p[pre][pj].second.second;
                            int curk = (prek + pow10[n - i] * curj) % k;
                            if(dp[i][curj][curk] == -1) {
                                dp[i][curj][curk] = prej;
                                p[cur][++ptop[cur]] = {++cntl, make_pair(curj, curk)};
                            }
                        }
                    }
                }
            }
     
            register int cur = (n & 1);
            sort(p[cur] + 1, p[cur] + 1 + ptop[cur]);
            int res = -1;
            for(int pj = 1; pj <= ptop[cur]; ++pj) {
                int curj = p[cur][pj].second.first;
                int curk = p[cur][pj].second.second;
                if(curk == 0) {
                    res = curj;
                    break;
                }
            }
     
            if(res != -1) {
                int curj = res, newj = -1;
                int curk = 0, newk = -1;
                for(int i = n; i >= 1; --i) {
                    ans[i] = curj;
                    newj = dp[i][curj][curk];
                    newk = ((curk - pow10[n - i] * curj) % k + k) % k;
                    curj = newj;
                    curk = newk;
                }
                for(int i = 1; i <= n; ++i)
                    printf("%d", ans[i]);
                printf("
    ");
            } else
                puts("-1");
        }
    }
    

    H - 3XOR

    有毒,明明是有序对,又给我说无序,真的有毒。

    要求所有有序三元组的异或和的和,考虑到每个位实际上是独立的,只考虑单独的某位k,那么它的贡献值就是它的值乘上使它为1的组合,那么就只有(1,1,1)和(0,0,1)两种咯,分别统计一下就可以了。

    不知道有没有处理部分O(1)的办法,反正这样肯定可以。感觉是有的,(1,1,1)的那种直接cnt1立方就行,然后(0,0,1)这样的,排一下就是3(cnt0)(cnt0)*(cnt1),非常简单。不过不用动脑是最好的。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
     
    int n;
    int a[10005];
    int cnt[2][21];
     
    int main() {
    #ifdef Yinku
        freopen("Yinku.in", "r", stdin);
    #endif // Yinku
        int n;
        while(~scanf("%d", &n)) {
            memset(cnt, 0, sizeof(cnt));
            for(int i = 1; i <= n; ++i) {
                scanf("%d", &a[i]);
                for(int k = 0; k <= 20; ++k)
                    ++cnt[(a[i] >> k) & 1][k];
            }
            ll sum = 0;
            for(int i = 1; i <= n; ++i) {
                for(int k = 0; k <= 20; ++k) {
                    if((a[i] >> k) & 1) {
                        sum += (1ll << k) * (cnt[0][k] * cnt[0][k]);
                        sum += (1ll << k) * (cnt[1][k] * cnt[1][k]);
                    } else {
                        sum += (1ll << k) * (cnt[0][k] * cnt[1][k]);
                        sum += (1ll << k) * (cnt[1][k] * cnt[0][k]);
                    }
                }
            }
            printf("%lld
    ", sum);
        }
    }
    
  • 相关阅读:
    1016: 写出来吧
    从硬件工程师转到纯软件开发,回顾那些岁月
    用大白话聊聊JavaSE -- 自定义注解入门
    数组中的一些常用方法总结
    js中的隐式转换
    开源OSS.Social微信项目进阶介绍
    .Net开源oss项目进度更新(含小程序接口)
    windows下部署免费ssl证书(letsencrypt)
    完成OSS.Http底层HttpClient重构封装 支持标准库
    谈javascript变量声明
  • 原文地址:https://www.cnblogs.com/Inko/p/11619859.html
Copyright © 2020-2023  润新知