借别人账号提交,多年没打cf,感觉智商上线?
A. Beru-taxi
给定人的坐标、n个出租车坐标和速度,出租车会按照这个速度向人开来,求最快什么时候能上到车。
代码找不到了,反正就是水题,暴力全部车子求距离/速度就行了,略
B. Interesting drink
略
C. Hard problem
给n个字符串和每个字符串反转一次需要的代价,求最小代价使得这n个字符串按字典序排列(字符串相同也认为是符合字典序),无解输出-1。
我们一直保持前i个字符串有序,dp搞就行了。
dp[i][0] : 当前字符串不旋转的情况下符合前i个字符串有序的最小代价;
dp[i][1] : 当前字符串旋转的情况下符合前i个字符串有序的最小代价。
则
dp[i][0] = min(dp[i-1][0],dp[i-1][1]) | str[i-1] < str[i]
dp[i][1] = cost[i] + min(dp[i-1][0],dp[i-1][1]) | str[i-1] < str[i]
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define clr( a , x ) memset ( a , x , sizeof (a) );
#define RE freopen("1.in","r",stdin);
#define WE freopen("1.out","w",stdout);
const int maxn = 100005;
const ll inf = 92233720368547807LL;
int val[maxn];
string s1[maxn];
ll dp[maxn][2];
int main() {
int n;
while (cin >> n) {
clr(val, 0);
for (int i = 1; i <= n; ++i) {
cin >> val[i];
}
for (int i = 1; i <= n; ++i) {
cin >> s1[i];
}
dp[1][0] = 0;
dp[1][1] = val[1];
dp[n][0] = dp[n][1] = inf;
for (int i = 2; i <= n; ++i) {
ll mx = inf;
if (s1[i - 1] <= s1[i]) {
mx = min(dp[i - 1][0], mx);
}
string last = s1[i - 1];
reverse(last.begin(), last.end());
if (last <= s1[i]) {
mx = min(dp[i - 1][1], mx);
}
dp[i][0] = mx;
ll my = inf;
string cur = s1[i];
reverse(cur.begin(), cur.end());
if (s1[i - 1] <= cur) {
my = min(val[i] + dp[i - 1][0], my);
}
if (last <= cur) {
my = min(val[i] + dp[i - 1][1], my);
}
dp[i][1] = my;
// cout << dp[i][0] << " " << dp[i][1] << endl;
}
ll ans = min(dp[n][0], dp[n][1]);
if (ans == inf) {
cout << "-1" << endl;
} else {
cout << ans << endl;
}
}
return 0;
}
D. Hard problem
给一个元素可重复的集合,初始有一个0元素在里面,现有三种操作(20W次):
1. + x 集合增加一个元素x
2. - x 集合删除一个元素x,数据保证删除时一定至少有一个x
3. ? x 查询集合元素和x的最大异或值,即
关键在于查询操作,我们不能直接遍历集合,现我们采用字典树+贪心可解决。
解:建一棵只有0和1的字典树,每加一个元素就将它按二进制插入树中,查询x时按二进制高位到低位求找树上点,为了使异或结果最大,我们总选择跟x的当前位值相反的点(异或不同时才为1,这样高位尽可能大总结果也就自然大),边找边把该结点的值累计上去就行了。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define clr( a , x ) memset ( a , x , sizeof (a) );
#define RE freopen("1.in","r",stdin);
#define WE freopen("1.out","w",stdout);
const int maxm = 4000100; //结点个数
struct node
{
int time;
int next[2]; //保存编号
void init() {
clr(next, 0); //0表示不存在结点,其他数字为编号
time = 0;
}
} node[maxm];
int cnt,tol;
void add(int x)
{
int u = 0, idx;
for (int i = 30; i >= 0; i--)
{
if ((1 << i) & x) idx = 1; else idx = 0; //该位数字
if (!node[u].next[idx]) //不存在就新建
{
node[u].next[idx] = ++tol;
node[tol].init();
}
u = node[u].next[idx]; //往下走
node[u].time++;
}
}
void del(int x)
{
int u = 0, idx;
for (int i = 30; i >= 0; i--)
{
if ((1 << i) & x) idx = 1; else idx = 0;
u = node[u].next[idx];
node[u].time--;
}
}
int query(int x) //查询树上点跟x的异或最大值
{
int u = 0, res = 0, idx;
for (int i = 30; i >= 0; i--)
{
if ((1 << i)&x) idx = 1; else idx = 0;
if (node[u].next[!idx] && node[node[u].next[!idx]].time > 0) //相反数且有次数
{
u = node[u].next[!idx];
res |= (1 << i); //加上路径上的值
} else u = node[u].next[idx];
}
return res; //求x^res最大跟求res最大一样
}
int main()
{
// RE
int n;
char op; int y;
scanf("%d%*c", &n);
tol = 0;cnt = 0;
node[0].init();
add(0);
for (int i = 0; i < n; ++i) {
scanf("%c %d%*c", &op, &y);
if (op == '+') {
add(y);
} else if (op == '-') {
del(y);
} else if (op == '?') {
printf("%d\n", query(y));
}
}
return 0;
}