1005 Rikka with Game
题意:给定一个字符串s,两个人轮流操作,每次操作有两种选择:1、终止操作进行结算;2、选择一个字符,将其变成下一个字符('a'->'b','b'->'c',...'z'->'a')。第一个操作的人想要s的字典序尽可能小,第二个操作的人想要s的字典序尽可能大,两个人都选择最优策略的情况下,求2^101轮之后的结果。
解题过程:一开始mxy推算出单个字符的情况,单个字符的情况下,'z'会在'b'的时候停止,而其他的字符是原样不动。我们确认后,以此提交了一发WA。但是很快我找到一个反例,就是"yz",第一个人一定会把'z'变成'a',而第二个人也不得不把'a'变成'b',由此我们猜测是要把第一个'z'变成'b',推算出更多的两个字符、三个字符的情况后,我们发现'y'字符是平衡的局面,大家都不敢动,直接进入下一个字符的博弈,而比'y'小的字符,第一个人一定会在第一步就宣布终止操作,比'y'大的字符,也就是'z',第一个人一定会把它变成'a',那么第二个人一定会把它变成'b',这个时候第一个人一定要选择结算了。
所以得到的策略就是,从左往右选择第一个非'y'的字符,假如它是'z',则变成'b',无论是不是'z',非'y'或者遇到字符串结尾就终止。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char s[100 + 5];
int main() {
#ifdef Inko
freopen("Inko.in", "r", stdin);
#endif // Inko
int T;
scanf("%d", &T);
while(T--) {
scanf("%s", s);
for(int i = 0; s[i] != ' '; ++i) {
if(s[i] != 'y') {
if(s[i] == 'z')
s[i] = 'b';
break;
}
}
puts(s);
}
}
1006 Rikka with Coin
题意:有10美分、20美分、50美分、1美元(100美分)的硬币,每种硬币有无限种。有n道菜,Rikka要对其中一道菜买单且不需要对方找钱,求最少要带多少硬币,无解输出-1。
解题过程:首先特判掉无解,全部除以10。然后就开始演了,想了很多种做法,xy想了一种贪心的,但是我感觉正确性很假。最后发现几个事实:10美分最多取1,20美分最多取3,50美分最多取1。枚举这些取法,暴力dp出能表示出的数,然后剩下的用100美分来补。
一开始这个遇到一点问题,就是小钱可能凑出一些100美分,使得后面可以少一枚100美分。比如12400,900,1100。900和1100py一下出现了10,20,20,50,和若干100,那么只需要123枚100。
解决的办法是留下最后不超过500美分的用小钱来凑,凑不出则尝试多取一枚100美分再凑。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t, n;
int a[105];
const int INF = 1e9;
int ans;
bool vis[1000];
void update(int d1, int d2, int d5, bitset<55> b) {
int maxans = 0;
int tmp = d1 + d2 + d5;
for(int i = 1; i <= n; ++i) {
bool suc = false;
if(a[i] <= 50) {
int cur = 0;
int ca = a[i];
while(ca >= 0) {
if(b[ca]) {
maxans = max(maxans, cur + tmp);
suc = true;
break;
} else {
ca -= 10;
cur += 1;
}
}
} else {
int ca = a[i] % 10;
while(ca < 30)
ca += 10;
int cur = (a[i] - ca) / 10;
while(ca >= 0) {
if(b[ca]) {
maxans = max(maxans, cur + tmp);
suc = true;
break;
} else {
ca -= 10;
cur += 1;
}
}
}
if(suc == false)
return;
}
ans = min(ans, maxans);
}
void dfs(int d1, int d2, int d5) {
if(d1 >= 2 || d2 >= 4 || d5 >= 2)
return;
if(vis[d1 * 100 + d2 * 10 + d5])
return;
vis[d1 * 100 + d2 * 10 + d5] = true;
bitset<55>nb(0);
for(int j = 0; j <= d1; ++j) {
for(int k = 0; k <= d2; ++k) {
for(int l = 0; l <= d5; ++l) {
if(j + 2 * k + 5 * l >= 51)
break;
nb[j + 2 * k + 5 * l] = true;
}
}
}
update(d1, d2, d5, nb);
dfs(d1 + 1, d2, d5);
dfs(d1, d2 + 1, d5);
dfs(d1, d2, d5 + 1);
}
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
bool suc = true;
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
if(a[i] % 10)
suc = false;
a[i] /= 10;
}
if(!suc) {
puts("-1");
continue;
}
ans = INF;
memset(vis, false, sizeof(vis));
dfs(0, 0, 0);
printf("%d
", ans);
}
}
1002 Rikka with Cake
题意:给定一块长方形的蛋糕,在上面用射线切开,求切成了几份。保证每条射线的端点都不在边界上,且任意两条射线满足 (x_i eq x_j space and space y_i eq y_j) ,也就是说,射线之间必然只有X型相交,不会出现T型相交或者L型相交。
补题过程:应用平面图的欧拉公式 (V-E+F=2) ,首先交点分为几种,射线之间是X型交点,射线与边界之间可以认为是T型交点(不再是射线,认为是从边界出发的线段),边界之间是L型交点。X型交点增加两条线段,T型交点增加一条线段,L型交点不增加线段。另外射线的端点称为O型点。根据题目的限制,以上点都不会重合。采用树状数组暴力统计X型交点。
首先对所有射线离散化,恰好是k个,初始化树状数组并注意设置树状数组的上界为k而不是n。把射线按y从小到大排序,先处理向上指的射线,从最高的y往下假如左右指向射线,遇到向上指的射线则询问其左侧有多少右指向线及其右侧有多少左指向线。向下则反过来。注意当射线可能重合或者有什么别的问题的时候这个可能会出bug。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXK = 100005;
int n, m, k;
struct Line {
int x, y;
char d;
Line() {}
Line(int x, int y, char d): x(x), y(y), d(d) {}
bool operator<(const Line &l)const {
return y < l.y;
}
} l[MAXK];
int x[MAXK], y[MAXK], tmp[MAXK];
char d[MAXK];
int cx;
int Lbit[MAXK], Rbit[MAXK];
inline void add(int *bit, int x) {
for(; x <= cx; x += x & -x)
++bit[x];
}
inline int sum(int *bit, int x) {
int res = 0;
for(; x; x -= x & -x)
res += bit[x];
return res;
}
int main() {
#ifdef Inko
freopen("Inko.in", "r", stdin);
#endif // Inko
int T;
scanf("%d", &T);
while(T--) {
scanf("%d%d%d", &n, &m, &k);
for(int i = 1; i <= k; ++i) {
char _d[2];
scanf("%d%d%s", &x[i], &y[i], _d);
d[i] = _d[0];
}
for(int i = 1; i <= k; ++i)
tmp[i] = x[i];
sort(tmp + 1, tmp + 1 + k);
cx = unique(tmp + 1, tmp + 1 + k) - (tmp + 1);
for(int i = 1; i <= k; ++i)
x[i] = lower_bound(tmp + 1, tmp + 1 + cx, x[i]) - tmp;
for(int i = 1; i <= k; ++i)
tmp[i] = y[i];
sort(tmp + 1, tmp + 1 + k);
int cy = unique(tmp + 1, tmp + 1 + k) - (tmp + 1);
for(int i = 1; i <= k; ++i)
y[i] = lower_bound(tmp + 1, tmp + 1 + cy, y[i]) - tmp;
for(int i = 1; i <= k; ++i)
l[i] = Line(x[i], y[i], d[i]);
sort(l + 1, l + 1 + k);
ll Vx = 0;
memset(Lbit, 0, sizeof(Lbit[0]) * (cx + 1));
memset(Rbit, 0, sizeof(Rbit[0]) * (cx + 1));
int sumLbit = 0;
for(int i = k; i >= 1; --i) {
if(l[i].d == 'L') {
add(Lbit, l[i].x);
++sumLbit;
} else if(l[i].d == 'R') {
add(Rbit, l[i].x);
} else if(l[i].d == 'U') {
Vx += sumLbit - sum(Lbit, l[i].x - 1);
Vx += sum(Rbit, l[i].x);
}
}
memset(Lbit, 0, sizeof(Lbit[0]) * (cx + 1));
memset(Rbit, 0, sizeof(Rbit[0]) * (cx + 1));
sumLbit = 0;
for(int i = 1; i <= k ; ++i) {
if(l[i].d == 'L') {
add(Lbit, l[i].x);
++sumLbit;
} else if(l[i].d == 'R') {
add(Rbit, l[i].x);
} else if(l[i].d == 'D') {
Vx += sumLbit - sum(Lbit, l[i].x - 1);
Vx += sum(Rbit, l[i].x);
}
}
ll Vt = k;
ll Vl = 4;
ll Vo = k;
ll E = 4ll + k;
E += Vx * 2ll; //x型交点
E += Vt; //T型交点
ll V = Vx + Vt + Vl + Vo; //把射线端点,T型交点和L型交点加上
ll F = 2ll + E - V;
printf("%lld
", F - 1);
}
}
1003 Rikka with Mista
补题过程:经过昨天ao神的提醒,使得某位为4的确实是一段连续的区间。一开始非常暴力,对A集的每个元素都二分找到B集中的左右位置,果断T了。然后按照题解搞个尺取,把AB集都后缀排序之后,尺取这个位置。注意比如自己是280,要排百位是4,那么下界应该是400-280=120,上界是499-280=219,这个上届要找的就是大于219的第一个位置,在这里演了好久。
一些小优化:两个集合实际上是最大值不会超过int的,这里可以卡掉两倍常数,其次,因为实际上只有100到108位有数字,也就是只需要排[0,8]就可以了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t, n, w1[20], w2[20];
int sum1[1 << 20], sum2[1 << 20];
int n1, n2;
void init() {
memset(sum1, 0, sizeof(sum1));
memset(sum2, 0, sizeof(sum2));
scanf("%d", &n);
n1 = n / 2, n2 = n - n1;
for(int i = 0; i < n1; ++i) {
scanf("%d", &w1[i]);
sum1[1 << i] = w1[i];
}
for(int i = 0; i < n2; ++i) {
scanf("%d", &w2[i]);
sum2[1 << i] = w2[i];
}
for(int i = 1; i < (1 << n1); ++i) {
int j = i & (-i);
if(j != i)
sum1[i] = sum1[i ^ j] + sum1[j];
}
for(int i = 1; i < (1 << n2); ++i) {
int j = i & (-i);
if(j != i)
sum2[i] = sum2[i ^ j] + sum2[j];
}
}
int tmp[1 << 20];
int base[10];
int sum3[1 << 20];
int sum4[1 << 20];
void radix_sort1(int r) {
int n = (1 << n1);
memset(base, 0, sizeof(base));
for(int i = 0; i < n; ++i)
base[(sum1[i] / r) % 10]++;
for(int i = 1; i < 10; ++i)
base[i] += base[i - 1];
for(int i = n - 1; i >= 0; --i)
tmp[--base[(sum1[i] / r) % 10]] = sum1[i];
for(int i = 0; i < n; ++i)
sum1[i] = tmp[i];
int r10 = r * 10;
for(int i = 0; i < n; ++i)
sum3[i] = sum1[i] % r10;
}
void radix_sort2(int r) {
int n = (1 << n2);
memset(base, 0, sizeof(base));
for(int i = 0; i < n; ++i)
base[(sum2[i] / r) % 10]++;
for(int i = 1; i < 10; ++i)
base[i] += base[i - 1];
for(int i = n - 1; i >= 0; --i)
tmp[--base[(sum2[i] / r) % 10]] = sum2[i];
for(int i = 0; i < n; ++i)
sum2[i] = tmp[i];
int r10 = r * 10;
for(int i = 0; i < n; ++i)
sum4[i] = sum2[i] % r10;
}
ll calc(ll r) {
ll sum = 0;
int cn1 = (1 << n1), cn2 = (1 << n2);
int jl, jr; //[jl,jr)区间内是对i的合法
//找4
jl = lower_bound(sum4, sum4 + cn2, r * 4 - sum3[0]) - sum4;
//记得要-1,因为这个是upper_bound
jr = upper_bound(sum4, sum4 + cn2, (r * 5 - sum3[0]) - 1) - sum4;
sum += jr - jl;
for(int i = 1; i < cn1; ++i) {
while(jl - 1 >= 0 && sum3[i] + sum4[jl - 1] >= r * 4)
--jl;
while(jr - 1 >= 0 && sum3[i] + sum4[jr - 1] >= r * 5)
--jr;
sum += jr - jl;
}
//找14
jl = lower_bound(sum4, sum4 + cn2, r * 14 - sum3[0]) - sum4;
//记得要-1,因为这个是upper_bound
jr = upper_bound(sum4, sum4 + cn2, (r * 15 - sum3[0]) - 1) - sum4;
sum += jr - jl;
for(int i = 1; i < cn1; ++i) {
while(jl - 1 >= 0 && sum3[i] + sum4[jl - 1] >= r * 14)
--jl;
while(jr - 1 >= 0 && sum3[i] + sum4[jr - 1] >= r * 15)
--jr;
sum += jr - jl;
}
return sum;
}
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
scanf("%d", &t);
while(t--) {
init();
int r = 1;
ll sum = 0;
//只有第0位到第8位有数字
for(int i = 0; i <= 8; ++i) {
radix_sort1(r);
radix_sort2(r);
sum += calc(r);
r *= 10;
}
printf("%lld
", sum);
}
}