来自学长的馈赠1 社论
Alice
注意到一奇一偶的情况只能变成俩奇数,于是对面再给变回一奇一偶, 直到有一个为 就可以赢了 .
于是一奇一偶必胜,两奇必败,两偶数的同时除以几个 即可,容易证明除因子不影响答案 .
Box
假设没有停止这个东西,可以一直取 . 设取完第 种颜色的球的次数为 ,则答案就是 .
min-max 容斥,则
根据神秘组合意义可以得到当 时 .
于是把 相等的写在一起,即得
可以 算,然而会超时 .
可以把逆元提出来最后算,这样就可以过了 .
或者说只会有 个本质不同的 作为分母,考虑使用线性求任意 个数的逆元之 Trick 即可 .
Common
Algorithm 1
不强于 2D - 2SUM 问题 .
首先定义 .
令 ,构造多项式:
于是考察卷积 ,则不难发现 .
那么我们暴力枚举一波 ,答案就可以算了:
注意到模数是 ,于是可以一次 NTT 求 ,别的操作都不超过 .
总复杂度 ,常数非常大,跑不进 2s TAT .
Algorithm 2
注意到 矩阵是由递推形式给出 .
于是可以在 上操作,相当于 次操作一起做 .
具体的,对于每个 ,在 上加一,递推后在 处统计答案即可 .
负下标可以平移解决 .
Do not ak
题目链接 .
没学过分块.psd
开摆!
UPD. 我还是补补吧 😃
UPD. Give up.
UPD. Never Gonna Give You Up!!!
时间复杂度 空间复杂度 记 .
Tip. 统计两个有序数组间产生的逆序对个数可以归并(即归并排序的合并两数组部分) .
首先分块,块长设为 .
那么就有两种情况:
Case 1 左右端点 不在同一块内 .
考虑一下逆序对贡献的产生:
如图, 是左右散块, 是若干整块,于是就有贡献:
- TT:散块自己内部贡献 .
- BB:整块自己内部贡献 .
- BT:散块对整块贡献 .
- LR:散块间贡献 .
对于 TT,我们处理一个 ,表示 到其所在块左端点 / 右端点的逆序对贡献,这个可以权值树状数组扫一遍得到 .
这样就可以直接干了,总复杂度 .
对于 BT,我们只需要对于左侧散块的每个数 ,统计中间有多少个数 ;对于右侧散块每个数 ,统计中间有多少个数 .
这个可以 预处理前缀和 .
对于 LR,块内排序,直接归并即可, .
对于 BB,考虑预处理出所有答案 .
双指针,设目前左右端块为 ,则直接加上以前有的贡献,再枚举块内元素 ,用以前维护过的东西求出前面若干块内 即可 .
复杂度 .
Case 2 左右端点 在同一块内 .
拆成两个前缀逆序对之差和块间逆序对,前者预处理,后者归并 .
于是 .
取 得最优复杂度 .
Code
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
constexpr int N = 1e5 + 7, B = 514;
int n, q, a[N], pre[N], suf[N], buc[N], c[N], lt[B][N], bl[B][B];
int bs, bcnt, L[B], R[B], bel[N];
pii b[B][B];
struct FenwickTree
{
int f[N];
static inline int lowbit(int x){return x & -x;}
inline void clear(){memset(f, 0, sizeof f);}
inline void add(int x, int a){for (int i=x; i<=n; i+=lowbit(i)) f[i] += a;}
inline int query(int x)const{int ans = 0; for (int i=x; i; i&=i-1) ans += f[i]; return ans;}
}F;
inline void init()
{
for (int i=1; i<=bcnt; i++)
{
L[i] = (i-1) * bs + 1; R[i] = min(i*bs, n);
for (int j=L[i]; j<=R[i]; j++) bel[j] = i;
}
for (int i=1; i<=n; i++) b[bel[i]][i-L[bel[i]]+1] = make_pair(a[i], i);
for (int i=1; i<=bcnt; i++) stable_sort(b[i]+1, b[i]+R[i]-L[i]+2);
for (int i=1; i<=bcnt; i++)
{
F.clear(); ll sum = 0;
for (int j=L[i]; j<=R[i]; j++){pre[j] = (sum += j - L[i] - F.query(a[j])); F.add(a[j], 1);}
F.clear(); sum = 0;
for (int j=R[i]; j>=L[i]; j--){suf[j] = (sum += F.query(a[j])); F.add(a[j], 1);}
}
for (int i=1; i<=bcnt; i++)
{
int minn = n+1, s = 0;
for (int j=L[i]; j<=R[i]; j++){++c[a[j]]; chkmin(minn, a[j]);}
for (int j=minn; j<=n; j++){s += c[j]; buc[j] += s;}
for (int j=L[i]; j<=R[i]; j++) --c[a[j]];
for (int j=1; j<=n; j++) lt[i][j] = buc[a[j]-1];
}
for (int i=1; i<=bcnt; i++)
for (int j=1; j<i; j++)
for (int k=L[i]; k<=R[i]; k++) bl[i][j] += R[j] - lt[j][k];
}
inline ll query(int l, int r)
{
if ((l<1) || (r<1) || (l>n) || (r>n) || (l>r)) return 0;
if (bel[l] == bel[r])
{
ll ans = pre[r];
int le = L[bel[l]], ri = R[bel[l]];
if (l != le) ans -= pre[l-1];
for (int i=1, c=0; i<=ri-le+1; i++)
{
int _ = b[bel[l]][i].second;
if (_ < l) ans -= c;
c += ((l <= _) && (_ <= r));
} return ans;
}
ll ans = suf[l] + pre[r];
int lb = bel[l], rb = bel[r], le = L[lb], ri = R[rb];
for (int i=lb+1; i<rb; i++) ans += pre[R[i]] + bl[i][i-1] - bl[i][lb]; // 整块间
for (int i=l; i<=R[lb]; i++) ans += lt[rb-1][i] - lt[lb][i];
for (int i=L[rb]; i<=r; i++) ans += L[rb] - R[lb] - 1 - (lt[rb-1][i] - lt[lb][i]);
for (int i=1, j=1, c=0; i<=R[lb]-le+1; i++)
{
while ((j <= ri - L[rb] + 1) && (b[rb][j].first < b[lb][i].first)){c += (b[rb][j].second <= r); ++j;}
if (b[lb][i].second >= l) ans += c;
}
return ans;
}
int main()
{
scanf("%d%d", &n, &q);
bs = 300; bcnt = (n-1) / bs + 1;
for (int i=1; i<=n; i++) scanf("%d", a+i);
init();
int l, r; ll lstans = 0;
while (q--)
{
scanf("%d%d", &l, &r); l ^= lstans; r ^= lstans;
printf("%lld\n", lstans = query(l, r));
} return 0;
}
以下是博客签名,正文无关
本文来自博客园,作者:yspm,转载请注明原文链接:https://www.cnblogs.com/CDOI-24374/p/16494970.html
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0)进行许可。看完如果觉得有用请点个赞吧 QwQ
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】