zoukankan      html  css  js  c++  java
  • 自嗨测试赛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
    ", (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
    ", 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
    ", ans);
    	return 0;
    }
    
    
  • 相关阅读:
    09-JS的事件流的概念(重点)
    08-jQuery的位置信息
    07-小米导航案例
    python-selector模块
    python--day9--异步IO、数据库、队列、缓存
    python--select多路复用socket
    python--gevent高并发socket
    python--协程
    python--进程锁、进程池
    python--多进程
  • 原文地址:https://www.cnblogs.com/Lour688/p/14507961.html
Copyright © 2011-2022 走看看