6 problems, 3 accepted.
2h.
vp.
A. The Ultimate Square
猜结论 - AC
看样例猜结论。
cin >> n;
cout << (n + 1) / 2 << "\n";
B. Diverse Substrings
枚举 - AC
最多10种数,所以一个diverse substring长度最多是100。
枚举,时间复杂度
O
(
100
n
)
O(100n)
O(100n)。
正当我以为找出了标算,我突然发现,有最多
1
0
4
10^4
104组数据。那么,上面的算法就会超时了。
1
0
4
10^4
104优化不了,而
n
≤
1
0
5
n\leq 10^5
n≤105,这怎么想都是不可能完成的。
想了半天,又突然发现,
It is guaranteed that the sum of n over all test cases does not exceed 10^5.
白想了。
while (tt--) {
ans = 0;
cin >> n;
for (int i = 1; i <= n; ++i) {
char ch; cin >> ch;
a[i] = ch - '0';
}
for (int i = 1; i <= n; ++i) {
map<int, int> vh; int cnt = 0, mx = 0;
for (int j = i; j && i - j + 1 <= 100; --j) {
if (vh[a[j]] == 0) ++cnt;
ckmax(mx, ++vh[a[j]]);
if (mx <= cnt) ++ans;
}
}
cout << ans << "\n";
}
C. Zero-Sum Prefixes
分析 - AC
0随便变。
一开始想,后面的位置的前缀和,会受到前面很多0的影响。难搞。
后来一想,不如在每个0,先把它的前缀和变成0,这就相当于删去了前面的所有数。具体地,把1 2 0 3 4
先变成1 2 -3 3 4
,不考虑前面的1 2
,就相当于0 3 4
了。
那么,把序列根据0,分割成几个互相独立的部分。2 0 1 -1 0 3
变成2
,0 1 -1
,0 3
。它们每个里面只有一个0,求前缀和是0的个数的最大值,简单,不讲。
可是我一开始却WA了,看了将近10min没找出来错误。后来花20min把后三题都大致看了一遍,又回来,发现没开long long。
while (tt--) {
m = 0, ans = 0;
cin >> n;
vector<ll>().swap(a[++m]);
for (int i = 1; i <= n; ++i) {
ll tmp; cin >> tmp;
if (!tmp) vector<ll>().swap(a[++m]);
a[m].push_back(tmp);
}
ll s = 0;
for (int i : a[1]) {
s += i;
if (!s) ++ans;
}
for (int i = 2; i <= m; ++i) {
map<ll, int> vh; int mx = 0; s = 0;
for (int j : a[i]) {
s += j;
ckmax(mx, ++vh[s]);
}
ans += mx;
}
cout << ans << "\n";
}
D. ConstructOR
分析 - AC(补)
不如让
x
=
x
∣
a
=
x
∣
b
x=x|a=x|b
x=x∣a=x∣b.
那么,问题就转化为,把
a
∣
b
a|b
a∣b的某些0变成1,使它变成d的倍数。
d的倍数,
k
d
kd
kd,k可以二进制表示。也就是说,二进制下,d的倍数是几个左移不同位数的d加起来的。
比如
a
∣
b
=
(
101111
)
2
,
d
=
(
101
)
2
a|b=(101111)_2,d=(101)_2
a∣b=(101111)2,d=(101)2,从低位往高位,算
x
x
x,
a
∣
b
a|b
a∣b本来的1不能变,根据这个条件,把x用d凑出来。
而无解的情况,即d末尾0个数比
a
∣
b
a|b
a∣b末尾0个数多。此时,d就够不到
a
∣
b
a|b
a∣b最低的1了。
int tt;
ll a, b, d;
ll lowbit(ll x) { return x & -x; }
int main() {
setIO("");
cin >> tt;
while (tt--) {
cin >> a >> b >> d;
a |= b;
if (lowbit(d) > lowbit(a)) {
cout << -1 << "\n";
continue;
}
ll lb = lowbit(d), x = 0;
d /= lb; a /= lb;
for (int i = 0; (x & a) != a; ++i) {
if ((a & (1 << i)) && !(x & (1 << i))) {
x += d << i;
}
}
cout << x * lb << "\n";
}
return 0;
}
E. Yet Another Array Counting Problem
RMQ、分治、树形dp - AC(赛后)
下称leftmost maximum为lm。
对于
[
l
,
r
]
[l,r]
[l,r],本题的条件只与lm有关,我们只能看lm了。
根据题意,若lm位置是mid,数组b中,在
[
l
,
r
]
[l,r]
[l,r]上,mid上的数大于所有它左边的数,大于等于所有它右边的数。
我们该看哪个区间的lm呢?先看
[
1
,
n
]
[1,n]
[1,n]的吧。根据上一行得到一个对b的约束条件。这显然不是全部。
不难发现一个性质:含有mid的
[
l
,
r
]
[l,r]
[l,r]的子区间的lm位置也是mid。就像全年级第一同样是全班第一。
看完
[
1
,
n
]
[1,n]
[1,n],如果再看一个包含mid的子区间,得到的条件不会变多。所以,下面应该看
[
1
,
m
i
d
−
1
]
,
[
m
i
d
+
1
,
n
]
[1,mid-1],[mid+1,n]
[1,mid−1],[mid+1,n]。像是分治算法。
对了,刚刚说,“在
[
l
,
r
]
[l,r]
[l,r]上,mid上的数大于所有它左边的数,大于等于所有它右边的数”,事实上,只需要知道mid大于左边子区间的lm,大于等于右边子区间的lm,就算是知道了所有。
综合上面的想法,算法便明晰了:不断拆分大区间,建二叉树,在树上做dp。
lm相当于RMQ问题,用st表。
定义
d
p
(
u
,
i
)
dp(u,i)
dp(u,i),点u的值最大是i,方案数。
d
p
(
u
,
i
)
=
d
p
(
l
c
,
i
−
1
)
×
d
p
(
r
c
,
i
)
+
d
p
(
u
,
i
−
1
)
dp(u,i)=dp(lc,i-1)\times dp(rc,i)+dp(u,i-1)
dp(u,i)=dp(lc,i−1)×dp(rc,i)+dp(u,i−1).
比赛时想出了标算,顿时感到这真是一道好题。
然而此时离比赛结束只剩10min了,没时间写代码。事实上,在查出C题的错误后,我有1h时间看剩下三题,但是自以为能够攻克D题,所以把大部分时间花在了D上,事实上,D这样的二进制题我做的并不多。而E对我来说长得倒是更好看一点,但我没有留太多时间。
另外,由于数据范围,本题要用vector<vector<int> >
,注意代码中对resize的使用以及初始化方法。vector<int>[]
也行。
int tt, n, m, a[MAXN], logn[MAXN], st[MAXN][MAXP];
vector<vector<ll> > dp;
int build(int l, int r) {
if (l > r) return 0;
int p = logn[r - l + 1], mid;
if (a[st[l][p]] >= a[st[r - (1 << p) + 1][p]]) mid = st[l][p];
else mid = st[r - (1 << p) + 1][p];
int lc = build(l, mid - 1), rc = build(mid + 1, r);
for (int i = 1; i <= m; ++i) {
dp[mid][i] = mod(mod(dp[lc][i - 1] * dp[rc][i]) + dp[mid][i - 1]);
}
return mid;
}
int main() {
setIO("");
cin >> tt;
while (tt--) {
cin >> n >> m;
dp.resize(n + 1); dp[0].resize(m + 1);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
dp[i].resize(m + 1);
for (int j = 0; j <= m; ++j) dp[i][j] = 0;
}
logn[1] = 0;
for (int i = 2; i <= n; ++i) {
logn[i] = logn[i / 2] + 1;
}
for (int i = 1; i <= n; ++i) st[i][0] = i;
for (int j = 1; j < MAXP; ++j) {
for (int i = 1; i <= n - (1 << j) + 1; ++i) {
if (a[st[i][j - 1]] >= a[st[i + (1 << j - 1)][j - 1]]) st[i][j] = st[i][j - 1];
else st[i][j] = st[i + (1 << j - 1)][j - 1];
}
}
for (int i = 0; i <= m; ++i) dp[0][i] = 1;
int rt = build(1, n);
cout << dp[rt][m] << "\n";
}
return 0;
}