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);
    }
}
原文地址:https://www.cnblogs.com/Inko/p/11619859.html