2013 Asia Regional Dhaka 解题报告

来源:互联网 发布:从python开始学编程pdf 编辑:程序博客网 时间:2024/04/30 09:23

Source

Vjudge
UVALive 6650 - UVALive 6660


A 水


B

题意
  N个display,每个相当于一个长度为L的B进制的数。开始都是0,每次可以按一个按钮,使得某个display对应位加1。没有进位,所以最多到B-1。且每个数最低位不能按。两个人玩游戏,如果有人按下按钮后使得局面所有数字的和能被3整除,则输。无法移动则平局。问游戏结果。

分析
  相当于局面上数字和有模3等于0、1、2的三个状态,按钮相当于之间的边,算算一共有多少边,再分类讨论。假设是m0,m1,m2,分别代表使得数字和增加模3等于0、1、2的按钮有多少次。边界就不讨论了,考虑m0=0,假设m1 < m2,那么先手走m1,之后就是m1-m2-m1-m2 … m1会被耗空,后手只能走m2而负。m1>=m2也类似。考虑有m0,m0如果是偶数个,并没有什么用,因为m0相当于是先后手做一个转换,如果是偶数个,先手做一次m0,后手也可以做一次进而抵消。如果是奇数个,假设m1 < m2,先手可以走m2,给对方留下必胜态,然后再用m0做先手转换让自己必胜。但m1和m2相差不超过2时就不成立了,再讨论一下即可。

代码

/*************************************************************************    > File Name: b.cpp    > Author: james47    > Mail:     > Created Time: Wed Aug 26 10:16:25 2015 ************************************************************************/#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <algorithm>using namespace std;#define F 0#define S 1#define D 2typedef unsigned long long ll;int T, cas = 0;ll m0, m1, m2;char msg[3][5] = {"M", "J", "Draw"};int solve(){    if (m0 == 0 && m1 == 0 && m2 == 0) return D;    if (m0 != 0 && m1 == 0 && m2 == 0) return S;    if (m1 == 0 || m2 == 0){        if (!m2) swap(m1, m2);        if (m2 <= 2) return D;        if (m0 % 2 == 0) return S;        else return F;    }    if (m0 % 2 == 0) return F;    else{        if (m1 > m2) swap(m1, m2);        if (m1 == m2) return S;        if (m1 + 1 == m2 || m1 + 2 == m2) return D;        return F;    }}int main(){    scanf("%d", &T);    while(T--){        m0 = m1 = m2 = 0;        int n, l, b;        scanf("%d", &n);        while(n--){            scanf("%d %d", &l, &b);            if (b % 3 == 0) m0 += (ll)(l-1) * (b-1);            else if (b % 3 == 1) m1 += (ll)(l-1) * (b-1);            else{                m2 += (ll)l/2 * (b-1);                m1 += (ll)(l-1)/2 * (b-1);            }        }        printf("Case %d: %s\n", ++cas, msg[solve()]);    }    return 0;}

C

题意
  N个点的无向连通图(N <= 100),边有权值,要求选择一部分的边,使得前K个点的度数为奇数,之后的点度数为偶数,求满足条件的最小边权和。

分析
  考虑选了一条路径,那么只有端点的度数奇偶性改变。这样我们可以将前K个点通过选取不相交的路径来两两匹配,满足条件。由于边的权值为正,所以我们选的路径肯定不会相交,否则不优。所以floyd预处理点到点最近距离然后跑一般图最大权匹配即可。
  根据我多日的检索,一般图最大权匹配应该是要用带权开花树来做的,但是不会写(可以参见15年集训队论文讲图匹配的,或者就是论文啊书啊之类的)。。然后下面这个做法似乎是来自uestc的某位神牛,转化后找图中负环,随机化的做法,不保证最优,复杂度不明,据说1s能跑500点。

代码就不贴了,直接参见这篇吧
http://blog.csdn.net/fipped/article/details/47060931


D 水


E

题意
  三类珠子,分别能涂X、Y、Z种颜色,第一种和第三种珠子共有A颗,第二种和第三种共B颗,给A、B、X、Y、Z(均<=10^17),问有多少种不同的珍珠链,结果模1000003

分析
  首先肯定要考虑枚举第三种珠子,确定一个长度。结果的式子不难列出:

i=0min(A,B)CAA+BiCiAXAiYBiZi

  但是范围太大了,做不了。看了ac代码感觉不明觉厉。然后博哥给我讲了讲,大概明白是怎么一回事。
  记 p = 1000003,考虑把i做p进制分解,由lucas定理我们知道前面的组合数是可以这么拆开再乘起来的,后面的幂次推一推发现也是可以的(Xapk=Xa mod p)~但是p进制每一位需要算0-p-1吗?这样结果也不太对。实际上对于推广的组合数有Cab=0, a>b,所以每位我们只要算到A和B对应位比较小的就好了,i超过A对应位apiCiapi=0,超过B对应位bpiCapiapi+bpii=0

代码

/*************************************************************************    > File Name: e.cpp    > Author: james47    > Mail:     > Created Time: Fri Aug 28 15:07:40 2015 ************************************************************************/#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <cassert>#include <algorithm>using namespace std;const int mod = 1000003;typedef long long ll;int T;ll a[mod*2+10], inv[mod*2+10], px[mod+10], py[mod+10], pz[mod+10];inline ll C(ll n, ll m){//  assert(n < mod && m < mod && n-m < mod);    return a[n] * inv[a[m]] % mod * inv[a[n-m]] % mod;}ll A, B, X, Y, Z;ll solve(ll A, ll B, ll X, ll Y, ll Z){    ll ret = 1;    X %= mod, Y %= mod, Z %= mod;    px[0] = py[0] = pz[0] = 1;    for (int i = 1; i < mod; i++){        px[i] = px[i-1] * X % mod;        py[i] = py[i-1] * Y % mod;        pz[i] = pz[i-1] * Z % mod;    }    while(A&&B){ //seems that while(A) is wrong        ll a = A % mod, b = B % mod;        A /= mod, B /= mod;        ll tmp = 0;        for (int i = 0; i <= a; i++){            if (i > b) break;            (tmp += C(a+b-i, a) * C(a, i) * px[a-i] % mod * py[b-i] * pz[i]) %= mod;        }        (ret *= tmp) %= mod;    }    return ret;}void init(){    a[0] = a[1] = 1; inv[1] = 1;    for (int i = 2; i < mod*2+5; i++){        a[i] = a[i-1] * i % mod;        inv[i] = inv[mod % i] * (mod - mod/i) % mod;    }//  cout << C(mod+10, 2) << endl;//  cout << a[mod+10] * inv[a[2]] % mod * inv[a[mod+8]] << endl;}int main(){    init();    ios::sync_with_stdio(false);    cin >> T;    while(T--){        cin >> A >> B >> X >> Y >> Z;        assert(solve(A, B, X, Y, Z) == solve(B, A, Y, X, Z));        cout << solve(A, B, X, Y, Z) << endl;    }    return 0;}

F

略 (x, y)逆时针转90度是(-y, x),然后调整即可。更聪明的做法是交换一下位置直接输出~


G

题意
  有N个区间[L, R],给M个询问X,对于满足L<=X<=R的区间,求max(min(X-L, R-X))

分析
  我们发现一个区间如果被其他区间包含,那么它就没有意义了。。去掉区间的嵌套,然后就发现区间是有管辖的范围,范围内的查询的答案都由它得到,而管辖的范围会是单调的,单调队列一样扫一遍就好了~O(n)哒~更容易想到的做法大概是用set之类的维护,扫的过程中碰到区间的中点然后做插入删除之类的操作。

代码

/*************************************************************************    > File Name: g.cpp    > Author: james47    > Mail:     > Created Time: Wed Aug 26 09:30:13 2015 ************************************************************************/#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <set>#include <algorithm>using namespace std;typedef set<pair<int, int> >::iterator sit;const int maxn = (int)1e5+100;int T, n, m, cas = 0;pair<int, int> a[maxn];int b[maxn], c[maxn];bool cmp(const int& x, const int& y){ return b[x] < b[y]; }int main(){    scanf("%d", &T);    while(T--){        scanf("%d %d", &n, &m);        for (int i = 0; i < n; i++)            scanf("%d %d", &a[i].first, &a[i].second);        sort(a, a+n);        int orin = n, n = 1;        for (int i = 1; i < orin; i++){            if (a[i].second <= a[n-1].second) continue;            if (a[i].first == a[n-1].first){ a[n-1] = a[i]; continue; }            a[n++] = a[i];        }//      for (int i = 0; i < n; i++)//          printf("%d %d\n", a[i].first, a[i].second);        for (int i = 0; i < m; i++){            scanf("%d", b+i);            c[i] = i;        }        sort(c, c+m, cmp);          int head = 0, tail = 0;        for (int i = 0; i < m; i++){            int x = b[c[i]]; int& ans = b[c[i]] = 0;            while(tail < n && a[tail].first <= x) tail ++;            while(head < tail && a[head].second < x) head ++;            if (head >= tail) continue;//          printf("%d %d %d %d %d\n", head, tail, a[head].first, a[head].second, x);            ans = min(a[head].second - x, x - a[head].first);            while(head+1 < tail){                int tmp = min(a[head+1].second - x, x - a[head+1].first);                if (tmp > ans){ ans = tmp; head++; }                else break;            }        }        printf("Case %d:\n", ++ cas);        for (int i = 0; i < m; i++)            printf("%d\n", b[i]);    }    return 0;}

H
题意
  Given an integer N, find how many pairs (A, B) are there such that: gcd(A, B) = A xor B where 1 ≤ B ≤ A ≤ N

分析
考虑A是两者中比较大的数,然后枚举它的因子C作为gcd,则对应的唯一可能的B=A xor C,可以直接判gcd(A, B) == C(先判整除,然后再求gcd,即可通过,见http://blog.csdn.net/firenet1/article/details/47981287)。

而实际上A=pC, B=qC, p > q, 由于a xor bab,即A xor B >= (p-q)C,只有p-q = 1,即只有唯一的B = A-C可能成立,所以判A^(A-C)==C即可

代码

/*************************************************************************    > File Name: h.cpp    > Author: james47    > Mail:     > Created Time: Wed Aug 26 10:49:10 2015 ************************************************************************/#include <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <algorithm>using namespace std;const int maxn = (int)3e7;int T, cas;long long ans[maxn+100];int main(){    for (int i = 1; i <= maxn; i++)        for (int j = i+i; j <= maxn; j += i){            if ((j^(j-i)) == i) ans[j]++;        }    for (int i = 2; i <= maxn; i++)        ans[i] += ans[i-1];    ios::sync_with_stdio(false);    cin >> T;    while(T--){        int n; cin >> n;        cout << "Case " << ++cas << ": " << ans[n] << endl;    }    return 0;}

I

题意
有一个人用Prim做Dijkstra做的事,请把输入数据做调整使得结果是正确的,输入为N点M边无向连通图,边权为正且互异
分析
用Prim求出了最小生成树,也就得到了起点到各个点的一条路,我们得让最短路刚好落在树上。。。反正就是bfs一下,从小到大赋权值。。
代码不贴了。。


J

挺简单的吧。。略过


K

dancing links。。。待补。。。

0 0
原创粉丝点击