修仙录 3.11

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_41709770/article/details/88391468

呵呵
10分垫底,本来还以为第三题打了40分的
鬼知道哪里re了
一来两道题就是4重求和
一点鲍丽芬都不给
小气

算了,只要代码量不触及到我的知识盲点
我就能改


jzoj 6051 Cubelia

https://jzoj.net/senior/#main/show/6051
这种题真的能在考场上想出来吗
因为代码简单就先改了
– 一个子段的最大前缀和位置,显然就是原序列最大前缀和数组中[L,R]区间最大值的位置(因为区间值相当于都是减了一个sum[L-1])
那么题目可以转换为求所有连续子序列的最大值的和,减去多出来的前缀和(因为其实直接算的是sum的和,还要减去sum[l-1])
多出来的部分可以 O(n) 预处理
然后你会发现这就是 [HNOI2016] 序列
哇塞,竟然是一样的
https://mogicianevian.github.io/2018/03/18/HNOI-2016-序列(RMQ-单调栈)/
主体部分RMQ在博客里讲的很详细了,我把唯一不同的部分讲讲

到底怎么预处理多出来的部分:
实际上我们发现,对于每个子序列[L,R]都要减去sum[L-1],
显然我们需要减 i = L R s u m [ i 1 ] ( R i + 1 ) \sum_{i=L}^Rsum[i-1]*(R-i+1)
因为R在变,直接不好处理,所以就变一下
i = L R s u m [ i 1 ] ( n i + 1 )     ( n R ) i = L R s u m [ i 1 ] \sum_{i=L}^Rsum[i-1]*(n-i+1)\ -\ (n-R)*\sum_{i=L}^Rsum[i-1]
现在把两坨分别用前缀和预处理就行了

这个题可以当成模板吧,RMQ很巧妙的

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=2e6+5;
const int mod=998244353;

int n,q,a[MAXN],sum[MAXN];
int S,A,B,P,tp;
long long ans,lastans;
long long pre[MAXN],Pre[MAXN],suf[MAXN],Suf[MAXN],lot_1[MAXN],lot_2[MAXN];
int LOG[MAXN],f[MAXN][25];
int st[MAXN],top;

int Rand(){
	S=(S*A%P+(B^(tp*lastans)))%P;
	S=S<0 ? -S : S;
	return S;
}

void prework(){
	LOG[1]=0;
	for(int i=2;i<=n;i++) LOG[i]=LOG[i/2]+1;
	for(int i=1;i<=n;i++) f[i][0]=i;
	for(int i=n;i>=1;i--) for(int j=1;j<=LOG[n-i+1];j++)
		f[i][j]= sum[f[i][j-1]]>sum[f[i+(1<<(j-1))][j-1]] ? f[i][j-1] : f[i+(1<<(j-1))][j-1];
	for(int i=n;i>=1;i--){
		while(top&&sum[i]>sum[st[top]]) top--;
		if(top) suf[i]=suf[st[top]]+1ll*(st[top]-i)*sum[i];
		else suf[i]=1ll*(n-i+1)*sum[i];
		Suf[i]=Suf[i+1]+suf[i];
		st[++top]=i;
	}
	top=0;
	for(int i=1;i<=n;i++){
		while(top&&sum[i]>sum[st[top]]) top--;
		if(top) pre[i]=pre[st[top]]+1ll*(i-st[top])*sum[i];
		else pre[i]=1ll*i*sum[i];
		Pre[i]=Pre[i-1]+pre[i];
		st[++top]=i;
	}
	for(int i=1;i<=n;i++) lot_1[i]=lot_1[i-1]+1ll*sum[i]*(n-i),lot_2[i]=lot_2[i-1]+1ll*sum[i];
}

int RMQ(int l,int r){
	int x=LOG[r-l+1],y=r-(1<<x)+1;
	return sum[f[l][x]]>sum[f[y][x]] ? f[l][x] : f[y][x];
}

int main(){
	freopen("cubelia.in","r",stdin);
	freopen("cubelia.out","w",stdout);
	cin>>n>>q;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
	prework();
	cin>>S>>A>>B>>P>>tp;
	while(q--){
		int l=Rand()%n+1,r=Rand()%n+1;
		if(l>r) swap(l,r);
		int x=RMQ(l,r);
		lastans=1ll*(x-l+1)*(r-x+1)*sum[x]
				+Suf[l]-Suf[x]-1ll*(x-l)*suf[x]
				+Pre[r]-Pre[x]-1ll*(r-x)*pre[x]
				-(lot_1[r-1]-(l==1?0:lot_1[l-2]))
				+(lot_2[r-1]-(l==1?0:lot_2[l-2]))*(n-r);
		ans=(ans+lastans+mod)%mod;
	}
	printf("%lld",ans);
	return 0;
}

jzoj 6052 Cuvelia

https://jzoj.net/senior/#main/show/6052
想复杂了啊啊啊啊
orz
各种乱七八糟的搞法都有

  • 1.虚树?
    明显没听懂 不过听说数据范围是这种: k i = = n \sum ki==n 可以向虚树方向考虑
  • 2.把中点拿来挪?
    明显不想打
    std题解:感性理解,绝望实现
    – 考虑两个点的情况,合法的点是两点中点以及中点出发不往两个点的子树中走能到的所有点。
    新加入点时,如果原中点对于新点不合法,我们只能把中点向新点的子树里挪动才能维持平衡,
    否则怎么挪都会不合法。所以可以用一个 set 维护当前所有点的 DFS 序来判断能不能走下去,然
    后倍增一下就行了。
    处理完所有点以后把没有询问点的子树大小加起来就是答案。
  • 3.正常人的想法(QAQ)
    仔细观察发现合法点是路径上的中点,除去属于路径上的点的其余子树的节点和
    k>2的话只需要O(n)跑一遍,最后取交集就行
    不用枚举每两个点,因为这显然是可传递,即:既满足a,b的点又满足b,c的点,显然也满足a,c
    接着就是集合该怎么维护,观察发现每次不合法的节点都是连续的,dfn正好是一段区间(哇塞)
    显然就可以用线段树维护啦,区间修改+求和嘛
    小技巧:在lca的时候只跳到目标点的深度+1点,就不用枚举来求路径上的点了(拜拜TLE)

害得我在考场上无限特判。。。
就差一点点就能AC的说(结果只有10分)
所以说仔细观察题目的隐藏性质是真的重要啊啊啊啊啊啊
虽然不一定观察的出来就是了

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=3e5+5;

int n,q,k,a[MAXN];
int head[MAXN],next[MAXN*2],to[MAXN*2],cnt;
int dfn[MAXN],lat[MAXN],tot,d[MAXN],fa[MAXN][20];
int t[5],w;
struct linetree{
	int sum,laz;
}tr[MAXN*4];

int read(){
	char x=getchar();int sum=0;
	while(x<'0'||x>'9') x=getchar();
	while(x>='0'&&x<='9') sum=sum*10+x-'0',x=getchar();
	return sum;
}

void add(int u,int v){
	next[++cnt]=head[u],to[cnt]=v,head[u]=cnt;
}

void dfs(int x,int F){
	dfn[x]=++tot;
	for(int i=1;i<=19;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int i=head[x];i;i=next[i]){
		int y=to[i];
		if(y==F) continue ;
		d[y]=d[x]+1,fa[y][0]=x;
		dfs(y,x);
	}
	lat[x]=tot;
}

void down(int p,int l,int r){
	if(tr[p].laz!=-1){
		int mid=(l+r)>>1,v=tr[p].laz;
		tr[p<<1].sum=v*(mid-l+1),tr[p<<1].laz=v;
		tr[p<<1|1].sum=v*(r-mid),tr[p<<1|1].laz=v;
		tr[p].laz=-1;
	}
}

void up(int p){
	tr[p].sum=tr[p<<1].sum+tr[p<<1|1].sum;
}

void build(int p,int l,int r){
	if(l==r){tr[p].laz=-1;return ;}
	int mid=(l+r)>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
}

void turn(int p,int l,int r,int L,int R,int v){
	if(l>=L&&r<=R) {tr[p].sum=v*(r-l+1),tr[p].laz=v;return ;}
	down(p,l,r);
	int mid=(l+r)>>1;
	if(L<=mid) turn(p<<1,l,mid,L,R,v);
	if(mid<R) turn(p<<1|1,mid+1,r,L,R,v);
	up(p);
}

int get_lca(int x,int y){
	if(d[x]<d[y]) swap(x,y);
	for(int i=19;i>=0;i--) if(d[fa[x][i]]>d[y]) x=fa[x][i];
	if(fa[x][0]==y) {t[++w]=x;return fa[x][0];}
	if(d[x]!=d[y]) x=fa[x][0];
	for(int i=19;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
	t[++w]=x,t[++w]=y;
	return fa[x][0];
}

int get_rt(int x,int l){
	int rt=x;
	for(int i=19;i>=0;i--) if(d[x]-d[fa[rt][i]]<l) rt=fa[rt][i];
	t[++w]=rt;
	return fa[rt][0];
}

void solve(int x,int y){
	if(tr[1].sum==n) return ;
	if(d[x]<d[y]) swap(x,y);
	w=0;
	int lca=get_lca(x,y);
	int l=d[x]+d[y]-2*d[lca];
	if(l%2) {turn(1,1,n,1,n,1);return ;}
	int rt=get_rt(x,l/2);
	if(d[x]!=d[y]){
		turn(1,1,n,dfn[t[w]],lat[t[w]],1);
		if(dfn[rt]!=1) turn(1,1,n,1,dfn[rt]-1,1);
		if(lat[rt]!=n) turn(1,1,n,lat[rt]+1,n,1);
	}
	else for(int i=1;i<=w;i++) turn(1,1,n,dfn[t[i]],lat[t[i]],1);
}

int main(){
	freopen("cuvelia.in","r",stdin);
	freopen("cuvelia.out","w",stdout);
	n=read(),q=read();
	for(int i=1;i<n;i++){
		int u=read(),v=read();
		add(u,v),add(v,u);
	}
	d[0]=-1,dfs(1,0);
	build(1,1,n);
	while(q--){
		k=read();
		for(int i=1;i<=k;i++) a[i]=read();
		if(k==1) {printf("%d\n",n);continue;}
		for(int i=2;i<=k;i++) solve(a[i-1],a[i]);
		printf("%d\n",n-tr[1].sum);
		turn(1,1,n,1,n,0);
	}
	return 0;
} 

今天改题量达标了呢
呵呵呵哈哈哈哈哈哈哇阿瓦阿瓦我咯咯咯咯
(第一题码量太大,没时间了,下次再说吧)

猜你喜欢

转载自blog.csdn.net/qq_41709770/article/details/88391468