T1
中文题意:
T组输入,每次输入一个字符串A,现在需要你找到一个字符串B,使得A+B(二进制加法)之后值最大。并且还有一个特殊的点在于,如果结果中存在连续的字符那么连续的字符会被浓缩成一个字符。
类似于A=111,B=111,得到结果会是A+B=222=2。现在要你输出字典序最大的结果是由哪个B得来的。
∣
A
∣
≤
1
0
5
|A|\leq10^5
∣A∣≤105
一个思路,高位能加1就加1,高位加了1之后要么是1要么是2,根据高位调整低位即可。
const int N = 1e6 + 7;
ll n, m;
char s[N];
void solve() {
n = read();
scanf("%s", s + 1);
rep(i, 1, n) {
char tmp = s[i];
tmp += 1;
if (tmp != s[i - 1]) printf("1");
else tmp -= 1, printf("0");
s[i] = tmp;
}
puts("");
}
T2
中文题意:
T组输入,每组输入一个d,需要你找到一个符合要求的整数k。整数k一定要有4个及以上因子,并且每两个因子之间差值的绝对值大于等于d。
T
≤
3000
,
d
≤
1
0
4
T\leq3000,d\leq10^4
T≤3000,d≤104。
题目要求任意两个因子之间都要相差,那么我们构造的时候就不需要构造合数了,合数还能拆分维护比较麻烦。那么我们想找到这样的整数k,首先1一定是k的因子,那么我们找到一个大于等于d+1的质数不就行了吗,对着这个找到的质数再找到第二个比他大d的质数,这样两个质数的乘积构造出来的答案k,一定可以拆成四个因子,分别是1, p r i m e 1 prime_1 prime1, p r i m e 2 prime_2 prime2, p r i m e 1 ∗ p r i m e 2 prime_1*prime_2 prime1∗prime2。并且最小的质数都是2,跳跃间隔找到的答案,相乘之后一定会比前一个大d。符合要求。
const int N = 1e6 + 7;
ll n, m;
ll a[N];
bool vis[N];
int prime[N], cnt;
void getprime() {
ms(vis, 1); vis[1] = 0;
rep(i, 2, N - 1) {
if (vis[i]) prime[++cnt] = i;
rep(j, 1, cnt) {
if (i * prime[j] >= N) break;
vis[i * prime[j]] = 0;
if (i % prime[j] == 0) break;
}
}
}
void solve() {
n = read();
m = *lower_bound(prime + 1, prime + 1 + cnt, n + 1);
ll ans = m;
m = *lower_bound(prime + 1, prime + 1 + cnt, m + n);
ans *= m;
print(ans);
}
int main() {
getprime();
int T = read(); while (T--)
solve();
return 0;
}
T3
中文题意:
T组输入,每组输入给出一组序列,你每次可以选择一个X,然后从这些序列中选取两个和为X的值删掉,并且下一次只能从删掉的两个值中选取更大的那个成为新的X重新进行上述操作,问这个序列能不能变为空的,并且如果可以变成空的,你第一步选取的X是多少,任意一个即可。
n
≤
1
0
3
,
a
i
≤
1
0
6
n\leq10^3,a_i\leq10^6
n≤103,ai≤106。
观察可以发现,我们每次都要选择一个数是另外两个数的和,那么第一次选取的时候一定要选择到最大的哪一个元素,不然后面永远都无法把他删掉。那么固定一个之后,再去枚举剩余的点。使用集合模拟即可通过。注意如果即将要删掉的两个元素相等的时候怎么判断,小心一点。
const int N = 1e6 + 7;
int n, m;
int a[N];
vector<int> calc(int x) {
multiset<int> st;
rep(i, 1, n * 2) st.insert(a[i]);
vector<int> res;
rep(i, 1, n) {
auto it1 = --st.end();
int y = *it1;
st.erase(it1);
auto it2 = st.find(x - y);
if (it2 == st.end()) return {};
res.push_back(y);
res.push_back(x - y);
x = max(y, x - y);
st.erase(it2);
}
return res;
}
void solve() {
n = read();
rep(i, 1, n * 2) a[i] = read();
sort(a + 1, a + 1 + n * 2);
rep(i, 1, n * 2 - 1) {
int x = a[i] + a[2 * n];
vector<int> res = calc(x);
if (res.size()) {
puts("YES");
print(x);
rep(j, 0, n - 1)
printf("%d %d\n", res[2 * j], res[2 * j + 1]);
return;
}
}
puts("NO");
}
T4
中文题意:
T组输入,给出长度为n的数组,每次你可以把相邻位置一起减1。现在你有一次使用超能力的机会,交换两个相邻位置,也就是
s
w
a
p
(
a
i
,
a
i
+
1
)
swap(a_i,a_{i+1})
swap(ai,ai+1),当然你也可以不使用超能力。在这样的前提下,问你是否可以把全部位置减为0。
n
≤
2
∗
1
0
5
,
a
i
≤
1
0
9
n\leq2*10^5,a_i\leq10^9
n≤2∗105,ai≤109。
前后缀差分。首先我们如果不使用能力的话,直接从前到后差分一次,再从后往前差分一次,判断是不是有最终差分值等于0的时候。如果有说明不使用超能力也可以全部变成0。我们假设前缀差分数组为 p r e [ i ] pre[i] pre[i],后缀差分数组为 s u f [ i ] suf[i] suf[i]。那么交换两个相邻位置显然对 [ 1 , i − 1 ] [1,i-1] [1,i−1]以及 [ i + 2 , n ] [i+2,n] [i+2,n]的区间答案是没有影响的。那么如果枚举到某个位置,它的前缀差分等于后一个位置的后缀差分,说明以这个位置作为终点不使用即可完成清零操作。如果要使用超能力呢,那么首先要保证交换后可以继续往后走。也就是 a i + 1 ≥ p r e i − 1 & & a i ≥ s u f i + 2 a_{i+1}\geq pre_{i-1} \&\&a_i\geq suf_{i+2} ai+1≥prei−1&&ai≥sufi+2。才可以把 i , i + 1 i,i+1 i,i+1两个位置清零,如果这样的前提下,可以使得前缀差分等于后缀差分,说明找到了解法,输出即可。
const int N = 1e6 + 7;
ll n, m;
ll a[N], pre[N], suf[N];
void solve() {
n = read();
pre[0] = suf[n + 1] = 0;
rep(i, 1, n) a[i] = read();
rep(i, 1, n) {
if (a[i] >= pre[i - 1]) pre[i] = a[i] - pre[i - 1];
else pre[i] = INF;
}
for (int i = n; i; --i) {
if (a[i] >= suf[i + 1]) suf[i] = a[i] - suf[i + 1];
else suf[i] = INF;
}
if (!pre[n] or !suf[1]) { puts("YES"); return; }
rep(i, 1, n - 1) {
if (pre[i] != INF and pre[i] == suf[i + 1]) {
puts("YES");
return;
}
if (a[i] >= suf[i + 2] and a[i + 1] >= pre[i - 1]
and a[i + 1] - pre[i - 1] == a[i] - suf[i + 2]) {
puts("YES");
return;
}
}
puts("NO");
}