第一场A题A 01 Sequence
题意:
有一个长度为n的首尾相接的01串,支持一种操作,选中si(si==1),将si和si左右两边的字符删除掉(一共删除三个字符),删除后相邻元素自动贴近,有q次询问,每次询问区间l,r(区间l,r也是首尾相接,并且保证区间长度是3的倍数),问最小需要将多少的0改成1才能把区间l,r全部删除干净。
分析:
计算操作数。
对于一个长度为len的区间,需要进行len / 3次操作才能删除干净(len一定是3的倍数),这是需要进行的操作数。
计算当前区间最多可支持的操作数sum,最后答案就是max(0, len/3 - sum)。
对于连续的k个1,最多进行k/2向上取整次操作,由此可计算出当前区间最多可支持的操作数。
解法有两种,第一种是预处理左右连续的1的个数和可操作数的前缀和,之后o(1)回答。第二种是直接利用线段树维护,线段树主要维护4个变量,区间内最左/右侧连续的1,除了最左/右连续的1之外,还可支持的操作数,区间是否全为1。
官方题解:
AC代码:
预处理左右连续的1的个数和可操作数的前缀和:
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false)
int n, q;
string s;
int sum[1000050];
int pre[1000050];
int last[1000050];
int cnt, tmp, st, num;
int l, r;
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> q;
cin >> s;
s = "!" + s;
for(int i = 1; i <= n; ++i)
{
if(s[i] == '1') ++cnt;
else cnt = 0;
sum[i] = sum[i - cnt - 1] + (cnt + 1) / 2;
pre[i] = cnt;
}
cnt = 0;
for(int i = n; i >= 1; --i)
{
if(s[i] == '1') ++cnt;
else cnt = 0;
last[i] = cnt;
}
while(q--)
{
cin >> l >> r;
if(last[l] >= r)
{
cout << 0 << '\n';
continue;
}
num = (last[l] + pre[r] + 1) / 2;
st = (r - l + 3) / 3;
num += sum[r - pre[r]] - sum[l + last[l]];
cout << max(0, st - num) << '\n';
}
return 0;
}
线段树维护:
#include <bits/stdc++.h>
using namespace std;
char ch[1000050];
int n, q;
int ar[1000050];
struct node
{
int l, r;
int numl, num, numr;
bool flag;
}tree[4000050];
int l, r;
int st, sum;
int calc(int x)
{
return (x + 1) >> 1;
}
node merge_node(const node &a, const node &b)
{
if(a.flag)
{
if(b.flag) return {a.l, b.r, a.numl + b.numl, 0, a.numr + b.numr, 1};
else return {a.l, b.r, a.numl + b.numl, b.num, b.numr, 0};
}
else
{
if(b.flag) return {a.l, b.r, a.numl, a.num, a.numr + b.numr, 0};
else return {a.l, b.r, a.numl, a.num + b.num + calc(a.numr + b.numl), b.numr, 0};
}
}
void build(int p, int l, int r)
{
if(l == r)
{
if(ar[l]) tree[p] = {l, r, 1, 0, 1, 1};
else tree[p] = {l, r, 0, 0, 0, 0};
return ;
}
int mid = (l + r) >> 1;
build(p<<1, l, mid);
build(p<<1|1, mid + 1, r);
tree[p] = merge_node(tree[p<<1], tree[p<<1|1]);
}
node query(int p, int l, int r)
{
if(l <= tree[p].l && tree[p].r <= r) return tree[p];
int mid = (tree[p].l + tree[p].r) >> 1;
if(r <= mid) return query(p<<1, l, r);
else if(l > mid) return query(p<<1|1, l, r);
node res1 = query(p<<1, l, r);
node res2 = query(p<<1|1, l, r);
return merge_node(res1, res2);
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> q;
cin >> ch + 1;
for(int i = 1; i <= n; ++i) ar[i] = ch[i] ^ 48;
build(1, 1, n);
while(q--)
{
cin >> l >> r;
node res = query(1, l, r);
st = (r - l + 3) / 3;
sum = res.num + calc(res.numl + res.numr);
cout << max(0, st - sum) << '\n';
}
return 0;
}
第二场A题Yet Another Remainder
题意:
有一个长度为n数字,求这个数字模p的值。
这个数字的每一位用bi表示。告诉你min(n,100)行数据,第i行i个数字,第1行一个数字表示b1+b2+…+bn的值,第二行两个数字分别表示b1+b3+b5+…,b2+b4+b6+…,第三行三个数字分别表示b1+b4+b7+…,b2+b5+b8+…,b3+b6+b9+…。以此类推
分析:
答案是从1~n求和 bi*10^(n-i) 后%p
10^(n-i)%p是按照一定规律循环出现的,把%p后相同的数字提出来,剩下的事一坨bi的相加,可以发现,对于每个%p后的数字要成的那一坨bi的累加正好就是题干输入的某一行,找出计算即可。
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int t, n, q, x;
vector<int> vt[105];
vector<int> tmp;
ll ans;
ll pow_mod(ll a, ll n, ll mod)
{
ll ans = 1;
a %= mod;
while(n)
{
if(n & 1) ans = (ans * a) % mod;
a = (a * a) % mod;
n >>= 1;
}
return ans;
}
void work_1()
{
while(q--)
{
scanf("%d", &x);
int bas = 1;
ans = 0;
for(int i = n - 1; i >= 0; --i)
{
ans = (ans + (vt[n][i] * bas) % x) % x;
bas = bas * 10 % x;
}
printf("%lld\n", ans);
}
}
void work_2()
{
while(q--)
{
scanf("%d", &x);
ans = 0;
tmp.clear();
tmp.push_back(pow_mod(10, n - 1, x));
int md;
for(int i = 2; i < n; ++i)
{
md = pow_mod(10, n - i, x);
//cout << i << ' ' << md << '\n';
if(md == tmp[0]) break;
else tmp.push_back(md);
}
int sum = tmp.size();
//cout << sum << '\n';
//for(int i = 0; i < sum; ++i) cout << tmp[i] << '\n';
for(int i = 0; i < sum; ++i)
{
ans = (ans + (vt[sum][i] * tmp[i]) % x) % x;
}
printf("%lld\n", ans);
}
}
int main()
{
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
for(int i = 0; i <= 100; ++i) vt[i].clear();
for(int i = 1; i <= min(n, 100); ++i)
{
for(int j = 1; j <= i; ++j)
{
scanf("%d", &x);
vt[i].push_back(x);
}
}
scanf("%d", &q);
if(n <= 100) work_1();
else work_2();
//work_2();
}
return 0;
}