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 2
typedef 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
分析
首先肯定要考虑枚举第三种珠子,确定一个长度。结果的式子不难列出:
但是范围太大了,做不了。看了ac代码感觉不明觉厉。然后博哥给我讲了讲,大概明白是怎么一回事。
记 p = 1000003,考虑把i
做p进制分解,由lucas定理我们知道前面的组合数是可以这么拆开再乘起来的,后面的幂次推一推发现也是可以的(
Xapk=Xa mod p
)~但是p进制每一位需要算0-p-1吗?这样结果也不太对。实际上对于推广的组合数有
Cab=0, a>b
,所以每位我们只要算到A和B对应位比较小的就好了,i
超过A对应位
api
则
Ciapi=0
,超过B对应位
bpi
则
Capiapi+bpi−i=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 b≥a−b ,即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。。。待补。。。