自嗨测试赛3
任意模数快速插值
分治,讨论最大值在左边/左右相等/在右边,且最小值在左边/左右相等/在右边,
注意最大,最小值在异侧的情况,可以先按最大值,将另一侧跑到极限,然后差分出满足最小值在另一侧的最小值之和
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn = 5e5+10;
const int mod = 998244353;
int n, ans;
int a[maxn], mx[maxn], mn[maxn], p[maxn], sum[maxn];
int read(int x = 0, bool f = 0, char ch = getchar()) {
for(;ch < '0' || ch > '9';ch = getchar()) f = ch=='-';
for(;ch >= '0' && ch <= '9';ch = getchar()) x = (x<<3)+(x<<1)+(ch&15);
return f ? -x : x;
}
void solve(int l, int r) {
if(l == r) return (ans += (ll)a[l]*a[l]%mod)%=mod, void();
int mid = (l+r)/2;
solve(l, mid), solve(mid+1, r);
mx[mid]=mn[mid]=a[mid], mx[mid+1]=mn[mid+1]=a[mid+1];
for(int i = mid-1;i >= l; --i) mx[i]=max(mx[i+1], a[i]), mn[i]=min(mn[i+1], a[i]);
for(int i = mid+2;i <= r; ++i) mx[i]=max(mx[i-1], a[i]), mn[i]=min(mn[i-1], a[i]);
for(int i=mid, j=mid+1;i >= l; --i) { // (x1+x2)*(y1+y2)
while(j<=r && mx[i]>=mx[j] && mn[i]<=mn[j]) ++j;
(ans += (ll)mx[i]*mn[i]%mod*(j-mid-1)%mod)%=mod;
}
for(int j=mid+1, i=mid;j <= r; ++j) { // x3*y3
while(i>=l && mx[i]<mx[j] && mn[i]>mn[j]) --i;
(ans += (ll)mx[j]*mn[j]%mod*(mid-i)%mod)%=mod;
}
for(int i=mid, j=mid+1;i >= l; --i) {
while(j<=r && mn[i]<=mn[j]) ++j;
p[i]=j;
}
sum[mid]=0;
for(int i=mid, j=mid+1;i >= l; --i) { // (x1+x2)*y3
while(j<=r && mx[i]>=mx[j]) sum[j]=(sum[j-1]+mn[j])%mod, ++j;
if(j-1 >= p[i]) (ans += (ll)mx[i]*(sum[j-1]-sum[p[i]-1])%mod)%=mod;
}
for(int j=mid+1, i=mid;j <= r; ++j) {
while(i>=l && mn[i]>mn[j]) --i;
p[j]=i;
}
sum[mid+1]=0;
for(int j=mid+1, i=mid;j <= r; ++j) { // x3*(y1+y2)
while(i>=l && mx[i]<mx[j]) sum[i]=(sum[i+1]+mn[i])%mod, --i;
if(i+1 <= p[j]) (ans += (ll)mx[j]*(sum[i+1]-sum[p[j]+1])%mod)%=mod;
}
}
int main() {
freopen("chazhi.in","r",stdin);
freopen("chazhi.out","w",stdout);
n = read();
for(int i = 1;i <= n; ++i) a[i] = read();
solve(1, n);
printf("%d\n", (ans%mod+mod)%mod);
return 0;
}
斗转星移
离线,将询问按时间排序,多维护几个标记,按优先级更新。
顺90度:(x,y) -> (y,-x)
逆90度:(x,y) -> (-y,x)
关于x=z对称:(x,y) -> \((2*z-x,y)\)
关于y=z对称:(x,y) -> \((x,2*z-y)\)
维护三个标记:1.交换标记 2.取负标记 3.增加标记
改一个标记的时候,按优先级依次更新就好了
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn = 1e6+10;
int n, m, q, op[maxn], z[maxn];
ll x[maxn], y[maxn], ansx[maxn], ansy[maxn], Add[2];
bool Fan[2], Swp;
struct Node {
int cnt, x, id;
bool operator < (const Node &B) const {
return cnt < B.cnt;
}
}a[maxn];
int read(int x = 0, bool f = 0, char ch = getchar()) {
for(;ch < '0' || ch > '9';ch = getchar()) f = ch=='-';
for(;ch >= '0' && ch <= '9';ch = getchar()) x = (x<<3)+(x<<1)+(ch&15);
return f ? -x : x;
}
void push_swap() {
swap(Add[0], Add[1]);
swap(Fan[0], Fan[1]);
}
int main() {
freopen("passing.in","r",stdin);
freopen("passing.out","w",stdout);
n=read();
for(int i=1;i <= n; ++i) x[i]=read(), y[i]=read();
m=read();
for(int i=1;i <= m; ++i) {
op[i]=read();
if(op[i]==3||op[i]==4) z[i]=read();
}
q=read();
for(int i=1;i <= q; ++i) a[i]=(Node){read(), read(), i};
sort(a+1, a+1+q);
for(int i=1, now=1;i <= m; ++i) {
if(op[i]==1) Swp^=1, push_swap(), Fan[1]^=1, Add[1]=-Add[1];
if(op[i]==2) Swp^=1, push_swap(), Fan[0]^=1, Add[0]=-Add[0];
if(op[i]==3) {
Fan[0]^=1;
Add[0]=-Add[0]+2ll*z[i];
}
if(op[i]==4) {
Fan[1]^=1;
Add[1]=-Add[1]+2ll*z[i];
}
while(now<=q && a[now].cnt==i) {
ll xx=x[a[now].x], yy=y[a[now].x];
if(Swp) swap(xx, yy);
if(Fan[0]) xx=-xx;
if(Fan[1]) yy=-yy;
xx+=Add[0], yy+=Add[1];
ansx[a[now].id]=xx;
ansy[a[now].id]=yy;
++now;
}
}
for(int i=1;i <= q; ++i) printf("%lld %lld\n", ansx[i], ansy[i]);
return 0;
}
如何更快出题
高维前缀和+Boruvka最小生成树
加入n+1号点,点权为0,将所有朋友连边得到了一张联通图,点为x和y则边权为a[x]+a[y],答案就是最大生成树权值-所有点权值之和。
不难分析出这一定是合法的:
-
最终的树上与n+1相连的点就是主动加入团队的
-
y被x连进树内,正好贡献a[x],以后被y连进来的,正好贡献a[y]
然后因为边数是n^2级别的,考虑Boruvka最小生成树,对于每次增广,用高位前缀和VlogV(V是值域)预处理,对于每个联通块,O(1)找到连向其他联通块的最优边。
预处理出f1[v],f2[v],表示满足以下三个条件的点x,y的编号
-
x,y不在同一联通块
-
a[x],a[y]都是v的子集
-
a[x],a[y]分别是满足条件1,2中的点的最大,次大
这样对于枚举点i,最优的出边连向的j一定是f1[maxs^a[i]],f2[maxs^a[i]]中的一个,因此对于每个点都能O(1)找到最优出边
Code
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10;
int n, cnt, mx, maxs=1, bit;
int a[maxn], fa[maxn], bl[maxn], f1[1<<20|10], f2[1<<20|10], tw[maxn], ta[maxn];
ll ans;
int read(int x=0, bool f=0, char ch=getchar()) {
for(;ch<'0' || ch>'9';ch=getchar()) f=ch=='-';
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch&15);
return f?-x:x;
}
int findrt(int x) {
return fa[x]==x?x:fa[x]=findrt(fa[x]);
}
void modify(int x, int id) {
if(id==-1) return;
int v=a[id], c=bl[id];
if(f1[x]==-1) f1[x]=id;
else if(a[f1[x]]<=v) {
if(f1[x]!=-1&&bl[f1[x]]!=c) f2[x]=f1[x];
f1[x]=id;
}
else if(f2[x]==-1&&c!=bl[f1[x]]) f2[x]=id;
else if(a[f2[x]]<=v&&c!=bl[f1[x]]) f2[x]=id;
}
int main() {
freopen("threat.in","r",stdin);
freopen("threat.out","w",stdout);
n=read();
for(int i=1;i<=n;++i) mx=max(mx, a[i]=read()), ans-=a[i];
while(maxs<=mx) maxs<<=1, ++bit;
--maxs, ++n;
for(int i=1;i<=n;++i) fa[i]=i;
while(cnt<n-1) {
memset(tw, -1, sizeof tw);
memset(f1, -1, sizeof(f1));
memset(f2, -1, sizeof(f2));
for(int i=1;i<=n;++i) {
bl[i]=findrt(i);
modify(a[i], i);
}
for(int i=0;i<=bit;++i) {
for(int j=0;j<=maxs;++j) {
if(j&(1<<i)) {
modify(j, f1[j^(1<<i)]);
modify(j, f2[j^(1<<i)]);
}
}
}
for(int i=1;i<=n;++i) {
int v=(maxs^a[i]);
int x=bl[i];
if(f1[v]!=-1&&bl[f1[v]]!=x) {
if(a[i]+a[f1[v]]>tw[x]) tw[x]=a[i]+a[f1[v]], ta[x]=bl[f1[v]];
}
else if(f2[v]!=-1&&bl[f2[v]]!=x) {
if(a[i]+a[f2[v]]>tw[x]) tw[x]=a[i]+a[f2[v]], ta[x]=bl[f2[v]];
}
}
for(int i=1;i<=n;++i) {
if(findrt(i)!=i) continue;
int x=findrt(ta[i]);
if(x==i) continue;
ans+=tw[i], ++cnt, fa[i]=x;
}
}
printf("%lld\n", ans);
return 0;
}