———题解————
A.Max add
两个前缀和
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 2e5 + 9;
const int mod = 1e9 + 7;
ll n, m, ans, x, k;
ll a[maxn];
ll f[maxn];
ll g[maxn];
int main()
{
cin >> n;
for(int i = 1; i <= n; ++i)
scanf("%lld", &a[i]), f[i] = f[i-1] + a[i], g[i] = f[i] + g[i-1];
ll Max = a[1];
for(int i = 1; i <= n; ++i)
{
Max = max(Max, a[i]);
printf("%lld\n", Max*i+g[i]);
}
return 0;
}
B - Uniformly Distributed
思维 找规律算贡献
规律:从右上到左下的副对角线上必须全为蓝色或者全为红色
这样一条对角线上的格子的坐标特征:i+j 相等
对于一条要么全是红格子或者蓝格子的副对角线,其对答案无贡献(有一个格子红色就相当于全红,一个是蓝色就相当于全蓝);对于一条全空的副对角线,其对答案的贡献为 2 (两种涂色方案);对于既有蓝色又有红色的副对角线,直接break输出0
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1000 + 9;
const int mod = 998244353;
ll n, m, ans, x, k;
ll r[maxn*2], b[maxn*2];
char s[maxn][maxn];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; ++i)
scanf("%s",s[i]+1);
ll ans = 1;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
if(s[i][j]=='R') ++r[i+j];
else if(s[i][j]=='B') ++b[i+j];
for(int i = 2; i <= n+m; ++i)
if(!r[i] && !b[i]) ans = (ans*2ll)%mod;
else if(b[i] && r[i]){
ans = 0;break;
}
printf("%lld\n", ans);
return 0;
}
C - Swaps 2
先考虑解的存在性
首先根据所给操作可以发现一个数往左走增大,往右走减小。设
A
i
A_i
Ai 新位置是
D
i
D_i
Di,则必然有
B
D
i
=
A
i
+
i
−
D
i
B_{D_i}=A_i+i-D_i
BDi=Ai+i−Di,移项得到等式
B
D
i
+
D
i
=
A
i
+
i
B_{D_i}+D_i=A_i+i
BDi+Di=Ai+i
可以发现两个等式相似,只需建立两个新序列
A
i
+
i
{A_i+i}
Ai+i 和
B
i
+
i
{B_i+i}
Bi+i,然后排序判断是否能 一 一 对应,就可以判断是否存在解了。
然后考虑解的最优性
若按照值为第一关键字排序,原下标为第二关键字排序,则我们不难证明,此时一一对应的结果是最优的,这样
D
i
D_i
Di 就找到了。
新开一个数组建立两个序列下标一一对应的关系
最后其实就是求解新建数组的逆序数
为什么求逆序数就是答案,参考火柴排队这道题目
树状数组求逆序数
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 5;
ll n, m, sum[MAXN], A[MAXN];
struct node
{
int val, id;
bool operator<(const node &b)const{
return val == b.val ? id < b.id : val < b.val;
}
} a[MAXN], b[MAXN];
ll getsum(int i)
{
ll ans = 0;while(i > 0) ans += sum[i], i -= i & (-i);return ans;
}
void update(int i, ll k)
{
while(i <= n) sum[i] += k, i += i & (-i);
}
int main()
{
//ios::sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; ++i) scanf("%d", &a[i].val),a[i].val+=i,a[i].id = i;
for (int i = 1; i <= n; ++i) scanf("%d", &b[i].val),b[i].val+=i,b[i].id = i;
sort(a + 1, a + 1 + n);sort(b + 1, b + 1 + n);
for(int i = 1; i <= n; ++i)
if(a[i].val != b[i].val) return printf("-1\n"), 0;
else A[b[i].id] = a[i].id;// 一 一 对 应
ll ans = 0;
for(int i = 1; i <= n; i++)// 树状数组求逆序数
ans += A[i]-1 - getsum(A[i] - 1), update(A[i], 1ll);
printf("%lld\n", ans);
return 0;
}
解法二:
归并排序求逆序对
其实就是 CDQ分治 的思路
code:
#include <bits/stdc++.h>
#define LL long long
using namespace std;
typedef long long ll;
const int MAXN = 2e5 + 5;
ll tree[MAXN << 2], mark[MAXN << 2], n, m, A[MAXN];
ll B[MAXN], ans;
struct node
{
int val, id;
bool operator<(const node &b)const{
return val == b.val ? id < b.id : val < b.val;
}
} a[MAXN], b[MAXN];
LL ms(int l, int r)// 板子
{
if(l >= r) return 0;
int m = (l + r) >> 1;//二分
LL t1 = ms(l, m), t2 = ms(m + 1, r);//获得前半部分 和 后半部分 的逆序对个数
LL t3 = 0;//两边归并的逆序对个数
int i = l, j = m + 1, p = l;//前半部分起点、后半部分起点、升序排序数组指针
while(i <= m && j <= r)//进行排序操作
{
if(A[i] > A[j])//如果前部分的某个元素大于后半部分某个元素
{
B[p++] = A[j++];//升序排序
t3 += m - i + 1;//因为l到m区间内中:i后的元素都比a[i]大,所以也比a[j]大,所以逆序对有m-i+1(包括自己)个
}
else B[p++] = A[i++];//升序排序
}
//完成剩下的归并操作
while(i <= m) B[p++] = A[i++];
while(j <= r) B[p++] = A[j++];
for(int i = l; i <= r; i++) A[i] = B[i];//将排序的值返回给a
return t1 + t2 + t3;//返回
}
int main()
{
//ios::sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; ++i) scanf("%d", &a[i].val),a[i].val+=i,a[i].id = i;
for (int i = 1; i <= n; ++i) scanf("%d", &b[i].val),b[i].val+=i,b[i].id = i;
sort(a+1,a+1+n);sort(b+1,b+1+n);
for(int i = 1; i <= n; ++i)
if(a[i].val != b[i].val) return printf("-1\n"), 0;
else A[b[i].id] = a[i].id;
// 到这一步题目分析基本完成,可以用线段树维护下标求答案
// 也可以用归并排序等方法求解逆序数即为答案
printf("%lld\n", ms(1, n));
return 0;
}