ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

@4 UOJ388

2022-07-13 22:03:02  阅读:104  来源: 互联网

标签:rt const int mid UOJ388 include MOD


鸽子固定器

题目描述

点此看题

解法

考虑这样一种暴力的想法:我们把物品按照 \(s\) 排序,枚举 \(\max s\),然后扫描 \(\min s\),过程中维护前 \(m\) 大的 \(v\)

考虑优化这个暴力,对于选取个数 \(<m\) 的情况,一定选取了一段连续的区间,暴力枚举所有这样的区间即可,那么我们只需要考虑选取个数恰好 \(=m\) 的情况。

关键的 \(\tt observation\) 是:\(\min s\) 减小的目的是为了达到 \(v\) 的替换。以前的暴力是显式地让 \(\min s\) 减少,不妨切换枚举主体,枚举的过程变为显式地替换 \(v\)

所以我们把所有物品按照 \(v\) 排序,从小到大地删除物品。设现在删除的物品为 \(x\),我们只需要取出 \(x\) 的前面 \(m-1\) 个物品 \(/\) 后面 \(m-1\) 个物品,枚举长度为 \(m\) 的区间计算,这代表了替换 \(x\) 的过程。

用链表实现数的删除和取出前驱后继,时间复杂度 \(O(nm)\)

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 200005;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,A,B,ans,s[M],l[M],r[M],b[M],id[M];
struct node{int x,y;}a[M];
int get(int x,int y) {return y==1?x:x*x;}
int calc(int a,int b) {return get(a,A)-get(b,B);}
signed main()
{
	n=read();m=read();B=read();A=read();
	for(int i=1;i<=n;i++)
		a[i].x=read(),a[i].y=read();
	sort(a+1,a+1+n,[&](node a,node b){return a.x<b.x;});
	for(int i=1;i<=n;i++)
		s[i]=s[i-1]+a[i].y,id[i]=i;
	for(int l=1;l<=m;l++)
		for(int i=1;i+l-1<=n;i++)
			ans=max(ans,calc(s[i+l-1]-s[i-1],a[i+l-1].x-a[i].x));
	sort(id+1,id+1+n,[&](int u,int v){return a[u].y<a[v].y;});
	for(int i=1;i<n;i++) r[i]=i+1,l[i+1]=i;
	for(int i=1;i<=n;i++)
	{
		int u=id[i],t=0;
		for(int j=1,p=l[u];j<m && p;j++,p=l[p])
			b[++t]=p;
		reverse(b+1,b+1+t);
		for(int j=1,p=r[u];j<m && p;j++,p=r[p])
			b[++t]=p;
		for(int j=1;j<=t;j++) s[j]=s[j-1]+a[b[j]].y;
		for(int j=1;j+m-1<=t;j++)
			ans=max(ans,calc(s[j+m-1]-s[j-1],a[b[j+m-1]].x-a[b[j]].x));
		r[l[u]]=r[u];
		l[r[u]]=l[u];
	}
	printf("%lld\n",ans);
}

配对树

题目描述

点此看题

解法

匹配的策略是很清晰的,因为我们想让匹配两点的 \(\tt lca\) 越深越好。进一步分析,我们自底向上地来做,子树内的点是能匹配则匹配,一个子树给父亲的未匹配点至多有一个。

高妙的算贡献方式:考虑每一条边,这一条边会贡献当且仅当这条边链接的子树里有奇数个选出的点。那么问题转化成对每一条边,有多少个长度为偶数的区间使得子树内有奇数个区间内的点。

对于一棵子树,把在子树中的点设置为 \(1\),不在子树中的点设置为 \(0\),问题转化成区间和为奇数的偶区间有多少个。写出条件,其实就是:\(i=j\bmod 2,s[j]-s[i]=1\bmod 2\)

考虑分奇偶位置维护,统计 \(s\) 为 \(0/1\) 的个数,就可以很方便地计算答案。推广到多棵子树的情况,我们维护一棵差分标记的线段树,按照题目给的序列初始化,做线段树合并即可,时间复杂度 \(O(n\log n)\)

总结

注意贡献的主体,比如本题如果计算 \(\tt lca\) 处的贡献是不好做的。

#include <cstdio>
#include <iostream>
using namespace std;
const int M = 100005;
const int N = 20*M;
const int MOD = 998244353;
#define ll long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,ans,tot,F[M],rt[M];
int cnt,ls[N],rs[N],f[N][2][2],s[N];
struct edge{int v,c,next;}e[M<<1];
void up(int x,int l,int r)
{
	s[x]=s[ls[x]]+s[rs[x]];
	int k=s[ls[x]]&1,mid=(l+r)>>1;
	for(int i=0;i<2;i++)
		for(int j=0;j<2;j++)
			f[x][i][j]=f[ls[x]][i][j]+
			f[rs[x]][i^k][j];
	if(!ls[x]) f[x][0][0]+=mid/2-(l-1)/2,
		f[x][0][1]+=(mid+1)/2-l/2;
	if(!rs[x]) f[x][k][0]+=r/2-mid/2,
		f[x][k][1]+=(r+1)/2-(mid+1)/2;
}
void ins(int &x,int l,int r,int p)
{
	if(!x) x=++cnt;
	f[x][0][0]=r/2-(l-1)/2;
	f[x][0][1]=(r+1)/2-l/2;
	if(l==r) {s[x]++;return ;}
	int mid=(l+r)>>1;
	if(mid>=p) ins(ls[x],l,mid,p);
	else ins(rs[x],mid+1,r,p);
	up(x,l,r);
}
int merge(int x,int y,int l,int r)
{
	if(!x || !y) return x+y;
	int mid=(l+r)>>1;
	ls[x]=merge(ls[x],ls[y],l,mid);
	rs[x]=merge(rs[x],rs[y],mid+1,r);
	up(x,l,r);return x;
}
void dfs(int u,int fa)
{
	for(int i=F[u];i;i=e[i].next)
	{
		int v=e[i].v,c=e[i].c;
		if(v==fa) continue;
		dfs(v,u);
		ans=(ans+(ll)f[rt[v]][0][0]*
		f[rt[v]][1][0]%MOD*c)%MOD;
		ans=(ans+(ll)f[rt[v]][0][1]*
		f[rt[v]][1][1]%MOD*c)%MOD;
		rt[u]=merge(rt[u],rt[v],1,m+1);
	}
}
signed main()
{
	n=read();m=read();
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read(),c=read();
		e[++tot]=edge{v,c,F[u]},F[u]=tot;
		e[++tot]=edge{u,c,F[v]},F[v]=tot;
	}
	for(int i=1;i<=m;i++)
		ins(rt[read()],1,m+1,i);
	dfs(1,0);
	printf("%d\n",ans);
}

标签:rt,const,int,mid,UOJ388,include,MOD
来源: https://www.cnblogs.com/C202044zxy/p/16475708.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有