A. Creating a Character
暴力枚举显然不行,推一下式子就出来了,注意一下边界条件。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int main() {
ios::sync_with_stdio(false); cin.tie(0);
int T; cin >> T;
while(T--) {
int s0, t0, x; cin >> s0 >> t0 >> x;
int p = s0 + t0 + x;
int t = (p + 1) / 2 - 1;
t = min(t, t0 + x);
cout << max(t - t0 + 1, 0) << '
';
}
return 0;
}
B. Zmei Gorynich
题意:
现在有一只怪物有(n)个头,现在有(n)种打击方式,每种打击方式用一个二元组((d_i,h_i))表示,表示可以打掉(d_i)个头(血腥),之后怪物会长出(h_i)个头。
若某一次打击中,怪物的头数小于等于(0),那么它就阵亡了。
现在问最少打击次数为多少,可以鲨掉这个怪物;不行就输出(-1)。
思路:
- 首先判断是否能够鲨掉这头怪物,则只需要找(h_i-d_i)的最小值即可,假设为(min),那么当(min>0)时,很显然我们不能打持久战,必须一击解决,判断一下即可。
- 之后我们肯定贪心来搞,就是(h_i-d_i)最小的方式来搞,可以让怪物掉头最快。
- 但一直这样掉可能没有在某一时刻绝杀好,所以枚举持久战回合数再加一次绝杀判断。
- 显然答案具有单调性,二分优化一波即可。
代码如下:
Code
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 105;
int n, x;
int d[N], h[N];
int main() {
ios::sync_with_stdio(false); cin.tie(0);
int T; cin >> T;
while(T--) {
cin >> n >> x;
int Min = INF;
for(int i = 1; i <= n; i++) {
cin >> d[i] >> h[i];
Min = min(h[i] - d[i], Min);
}
if(Min >= 0) {
int Max = *max_element(d + 1, d + n + 1);
if(Max >= x) {
cout << 1 << '
';
} else cout << -1 << '
';
continue;
}
int mx = *max_element(d + 1, d + n + 1);
int l = 0, r = 1e9 + 1;
while(l < r) {
int mid = (l + r) >> 1;
if(mid == 0) l = mid + 1;
else if(x + 1ll * mid * Min <= 0) r = mid;
else if(x + 1ll * (mid - 1) * Min - mx <= 0) r = mid;
else l = mid + 1;
}
cout << l << '
';
}
return 0;
}
C. The Number Of Good Substrings
题意:
定义(f(t))等于二进制串(t)所表示的十进制数。
现在给出多个串(t),现在求对于每个串,有多少对((l,r)),满足(r-l+1=f(t_lcdots t_r))。
思路:
- 思考可以发现,只有每个(1)有用,并且有用的(1)不会超过(20)个,因为串的长度最大为(10^5)。
- 所以直接枚举右端点,按(1)来跳左端点统计答案即可。
可以证明,对于一个数(x),(xgeq len),(len)表示(x)的二进制串长度,对于(i)位置上面的(1),假设其上面一个位置的(1)为(last[i]),那么答案就肯定在(last[i])~(i)之间产生。
所以直接暴力跳就行。
Code
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
char s[N];
int last[N];
int main() {
ios::sync_with_stdio(false); cin.tie(0);
int T; cin >> T;
while(T--) {
cin >> s + 1;
int n = strlen(s + 1);
for(int i = 1; i <= n; i++) last[i] = 0;
int lst = 0;
for(int i = 1; i <= n; i++) {
if(s[i] == '1') {
last[i] = lst;
lst = i;
} else last[i] = lst;
}
// for(int i = 1; i <= n; i++) cout << last[i] << ' ';
// cout << '
';
int ans = 0;
for(int i = n; i >= 1; i--) {
int now = 0;
for(int j = i; j >= 1; j = last[j]) {
if(s[j] == '0') continue;
if(i - j >= 20) break;
now += (1 << (i - j));
if(i - last[j] >= now) ++ans;
}
}
cout << ans << '
';
}
return 0;
}
D. Coloring Edges
题意:
给出一个有向图,现在给边染色,满足若存在环,那么环上的边不能为同一颜色。
思路:
我们可以首先将所有的边都染为(1),若存在环,那么只要让环上的一条边染上(2)即可。
但如果在一个环上随便染那肯定是不行的。
我们要找这样的一条边((u,v)),满足(v)点在当前的栈中,将这条边染色就行。
正确性?可以结合(dfs)树来思考,如果我们把上述所说的边删去,那么最后可以形成一颗树,这颗树上的颜色都为(1),对于环来说,就是某个结点向自己祖先连边。很显然,一个环中两个颜色都有。
实现的时候可以不用(dfs),直接判断一下点的大小关系即可,orz。
Code
#include <bits/stdc++.h>
#define MP make_pair
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 5000 + 5;
int n, m;
struct Edge{
int v, next, id;
}e[N];
int head[N], tot;
void adde(int u, int v, int id) {
e[tot].v = v; e[tot].next = head[u]; e[tot].id = id; head[u] = tot++;
}
int in[N];
void Exit() {
cout << 1 << '
';
for(int i = 1; i <= m; i++) cout << 1 << "
"[i == m];
exit(0);
}
bool chk[N];
int ans[N];
int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> m;
memset(head, -1, sizeof(head));
for(int i = 1; i <= m; i++) {
int u, v; cin >> u >> v;
adde(u, v, i);
++in[v];
ans[i] = (u > v ? 1 : 2);
}
queue <int> q;
for(int i = 1; i <= n; i++) if(!in[i]) q.push(i);
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = head[u]; ~i; i = e[i].next) {
int v = e[i].v;
if(--in[v] == 0) q.push(v);
}
}
int f = 0;
for(int i = 1; i <= n; i++) {
if(in[i]) f = 1;
}
if(!f) Exit();
cout << 2 << '
';
for(int i = 1; i <= m; i++) cout << ans[i] << "
"[i == m];
return 0;
}
E. Sum Queries?
题意:
给出(n)个数,满足(a_ileq 10^9)。
现在定义“平衡的集合”:对于集合中(可重复)的元素,列竖式将它们都加起来,最终得到的和每一位的数字都至少和集合中某个元素该位上面的数字相同。
现在有(q)个操作,每个操作可以修改一个数字,或者在区间([l,r])中找出最小的不平衡集合。
思路:
- 很显然这个题可以按位来考虑。
- 观察到对于区间([l,r]),选出来的集合中,只要有两个数即可。假如不平衡集合中存在两个以上的数,那么必然会存在两个数,它们自己也可以组成一个不平衡集合。
- 对于一位来说,显然两个都不为(0)的数加起来得到的答案不等于这两个中的其中一个。
- 那么就直接对每一位维护一颗线段树,并且维护区间最小、次小值,更新值时只用把该位不为(0)的数加进去即可。
实现的话其实每个树上的结点都可以定义为一个结构体,然后重载一下符号,这样就可以很方便地维护很多信息了。
我写的比较麻烦...
Code
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 2e5 + 5, MAX = 2e9 + 5;
int n, m;
int a[N];
struct node{
int Min[N << 2], Min2[N << 2];
}tr[10];
void push_up(int id, int o) {
int mn = tr[id].Min[o << 1], mn2 = tr[id].Min[o << 1|1];
if(mn > mn2) swap(mn, mn2);
tr[id].Min[o] = mn;
if(tr[id].Min2[o << 1] < mn2) mn2 = tr[id].Min2[o << 1];
if(tr[id].Min2[o << 1|1] < mn2) mn2 = tr[id].Min2[o << 1|1];
tr[id].Min2[o] = mn2;
}
void build(int id, int o, int l, int r) {
if(l == r) {
tr[id].Min[o] = tr[id].Min2[o] = INF;
return ;
}
int mid = (l + r) >> 1;
build(id, o << 1, l, mid);
build(id, o << 1|1, mid + 1, r);
push_up(id, o);
}
void update(int id, int o, int l, int r, int p, int op) {
if(l == r) {
if(op == 1) {
tr[id].Min[o] = a[p];
tr[id].Min2[o] = INF;
} else {
tr[id].Min[o] = tr[id].Min2[o] = INF;
}
return;
}
int mid = (l + r) >> 1;
if(p <= mid) update(id, o << 1, l, mid, p, op);
else update(id, o << 1|1, mid + 1, r, p, op);
push_up(id, o);
}
void add(int x, int i) {
int p = 0;
while(x) {
int q = x % 10;
x /= 10;
if(q) update(p, 1, 1, n, i, 1);
++p;
}
}
void del(int x, int i) {
int p = 0;
while(x) {
int q = x % 10;
x /= 10;
if(q) update(p, 1, 1, n, i, -1);
++p;
}
}
void query(int id, int o, int l, int r, int L, int R, int &mn, int &mn2) {
if(L <= l && r <= R) {
mn = tr[id].Min[o], mn2 = tr[id].Min2[o];
return;
}
int tmp = INF, tmp2 = INF, tmp3 = INF, tmp4 = INF;
int mid = (l + r) >> 1;
if(L <= mid) query(id, o << 1, l, mid, L, R, tmp, tmp2);
if(R > mid) query(id, o << 1|1, mid + 1, r, L, R, tmp3, tmp4);
mn = tmp, mn2 = tmp3;
if(mn > mn2) swap(mn, mn2);
if(tmp2 < mn2) mn2 = tmp2;
if(tmp4 < mn2) mn2 = tmp4;
}
int main() {
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> m;
for(int i = 0; i < 10; i++) build(i, 1, 1, n);
for(int i = 1; i <= n; i++) {
cin >> a[i];
add(a[i], i);
}
while(m--) {
int op; cin >> op;
if(op == 1) {
int p, x; cin >> p >> x;
del(a[p], p);
a[p] = x;
add(a[p], p);
} else {
int l, r; cin >> l >> r;
int ans = MAX;
int mn, mn2;
for(int i = 0; i < 10; i++) {
query(i, 1, 1, n, l, r, mn, mn2);
if(mn != INF && mn2 != INF) ans = min(ans, mn + mn2);
// cout << mn << ' ' << mn2 << '
';
}
if(ans == MAX) ans = -1;
cout << ans << '
';
}
}
return 0;
}