不太会给这题上tag(,但是我不会算贡献。
思路:
可以抽象成找2k内的区间重叠,拿一个指针去扫最小修改次数。
找出所有只改一次就可以到达的范围,开个桶记录不用改就能到的点,然后对区间排序,pq记录区间右端点,滑窗扫一遍所有区间去遍历2->2k(即所有可能成为的数)找最小的修改次数。
优先队列的状态太智慧了!
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
//#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define fi first
#define se second
#define inf 0x3f3f3f3f
const int N = 4e5 + 50;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
int a[N];
pii b[N];
int cnt[N];//桶排要开两倍k
void work() {
int n, k;
cin >> n >> k;
//mem(cnt, 0);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
for (int i = 1; i <= n / 2; ++i) {
cnt[a[i] + a[n - i + 1]]++;
b[i] = {min(a[i], a[n - i + 1]) + 1, max(a[i], a[n - i + 1]) + k};
}
sort(b + 1, b + 1 + n / 2);
int l = 1;
//最多就每个都修改一下
ll ans = n / 2;
priority_queue<int, vector<int>, greater<int> >q;
for (int i = 2; i <= 2 * k; ++i) {
//如果桶里没有那肯定不是最优情况
if (!cnt[i])
continue;
//如果当前组一次修改能到的最小值小于当前值,就说明修改一次至少是能到的,入队
while (l <= n / 2 && b[l].fi <= i) {
q.push(b[l].se);
l++;
}
//如果当前组一次能修改到的最大值小于当前值,就说明必须要两次修改,出队
while (!q.empty() && q.top() < i) {
q.pop();
}
//一次修改的个数-不用修改的个数+2*两次修改的个数
ll res = q.size() - cnt[i] + 2 * (n / 2 - q.size());
ans = min(ans, res);
}
//如果做不到遍历还可以还原现场
for (int i = 1; i <= 2 * k; ++i) {
cnt[i] = 0;
}
cout << ans << '\n';
}
signed main() {
io;
int t;
cin >> t;
while (t--) {
work();
}
return 0;
}
思路:
std感觉很像上次那个快速分解n!质因数的思想,我们找的贡献其实就是前缀有1的数的个数+前缀有11的数的个数+有111的数的个数……,我抽象成塔去理解,有了底层就上层直接加本层就可以。
或者直接算贡献。
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
//#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define fi first
#define se second
#define inf 0x3f3f3f3f
const int N = 3e5 + 50;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
void work() {
ll n;
cin >> n;
ll res = 0;
for (ll x = 1, i = 1; x <= n; x = x * 10 + 1, ++i) {//枚举1前缀
for (ll y = 1; x * y <= n; y *= 10) {//去找n以内有这个前缀的所有数
if (y == 1)
res += i;
else {
for (int j = 0; j < 10; ++j) {
if (j == 1)//只允许有x循环内的前缀
continue;
//如果这个数的下一个比n大就说明这个数不能跑满本轮
if (x * y + y / 10 * (j + 1) > n) {
res += i * max(0ll, (n - x * y - y / 10 * j + 1));
break;
}
//不然就可以跑满
else {
res += i * max(0ll, y / 10);
}
}
}
}
}
cout << res << '\n';
}
signed main() {
io;
work();
return 0;
}