首页 > 其他分享 >千山鸟飞绝

千山鸟飞绝

时间:2024-07-09 19:59:52浏览次数:14  
标签:int max a1 inline am1 山鸟飞 am2

千山鸟飞绝

题目描述

话说有一天 doyouloveme 和 vfleaking 到山里玩。谁知 doyouloveme 刚刚进山,所有的鸟儿竟被他的神犇气场给惊得全部飞走了。vfleaking 顿时膜拜不已。

这时鸟王用鸟语说道:「!@#$%…?」,安抚了一下众鸟的情绪。鸟王生性好斗,作出了一个决定:要排鸟布阵把刚才吓到它们的人类赶出山去。

每只鸟都有一个编号,都有一个威武值。每秒钟鸟王都会发一个命令,编号为 \(v\) 的鸟飞到 \((x,y)\) 去(坐标系原点是山顶,坐标单位为鸟爪)。鸟飞得很快,一秒之内就飞到了,可以看作是瞬间移动。如果编号为 \(v\) 的鸟和编号为 \(u\) 的鸟某一时刻处在同一位置,它们就会互相鼓励,增加各自的士气值和团结值。一只鸟的士气值等于此刻与它处在同一位置的鸟中的威武值的最大值,团结值等于此刻与它处在同一位置的鸟的只数。如果每一时刻都没有鸟与它处在同一位置,则士气值和团结值都为 \(0\)。要注意自己不能鼓励自己,计算士气值和团结值时不能算上自己。

\(t\) 秒钟后,doyouloveme 目测出了现在每只鸟的战斗力,于是感叹了一句:「不妙,我们得走了。」

正所谓团结的鸟儿一个顶俩,所以 doyouloveme 这样描述战斗力:一只鸟战斗力值等于它在 \(0\) 到 \(t\) 秒中士气值的最大值与团结值的最大值的乘积。注意不是乘积的最大值,而是最大值的乘积。

vfleaking 很想知道现在每只鸟的战斗力,但是他当然不会啦,于是他把这个任务交给了你来完成。

输入格式

第一行一个整数 \(n\),代表鸟的只数。(鸟王那家伙你可以完全忽视掉)

接下来 \(n\) 行,每行三个整数 \(w,x,y\) 描述每只鸟的威武值和初始坐标。第 \(i+1\) 行描述编号为i的鸟。

接下来一行有一个整数 \(t\),代表经过时间 \(t\) 秒。

接下来 \(t\) 行,每行三个整数 \(v,x,y\) 描述鸟王每秒的命令。

输出格式

一共 \(n\) 行,每行一个数,代表每只鸟的战斗力。

样例 #1

样例输入 #1

5
1 1 1
3 1 2
4 4 4
2 0 1
2 2 3
5
1 1 2
2 4 4
2 4 3
3 0 1
5 0 1

样例输出 #1

3
4
6
8
8

样例 #2

样例输入 #2

5
1803632939 1051911108 963670239
296082233 384714041 782958792
1706221977 1051911108 963670239
1890039364 -1429456864 794782986
1152753107 1932597483 1442217530
10
3 -1429456864 794782986
2 -1429456864 794782986
4 -1429456864 794782986
4 2062723523 -411953943
5 -1429456864 794782986
4 1051911108 963670239
4 1051911108 963670239
1 1051911108 963670239
1 1051911108 963670239
5 -1429456864 794782986

样例输出 #2

1890039364
3780078728
3780078728
3607265878
3412443954

提示

对于 \(100\%\) 的数据,\(1≤n≤30000\),\(0≤t≤300000\),坐标为整数,均在 \([-2^{31},2^{31})\) 内。

威武值为不超过 \(2^{31}-1\) 的非负整数。

解析

赛时打暴力,纯 \(vector\),跑的飞快,洛谷能过,题库 \(50pts\)。

code(内容过于暴力,不建议观看)
#include<bits/stdc++.h>
#include<vector>
using namespace std;
#define LL long long
#define rrr register
const int N = 4e5+5;
int n,m;
int cnt[N],tot,va[N],a1[N],a2[N],wh[N];
unordered_map<int,unordered_map<int,int> > mp;
vector<int> st[N];
inline pair<int,int> fmx(int k)
{
	int r1=0,r2=0;
	for(int i:st[k])
	{
		if(va[i]>=r1) r2=r1,r1=va[i];
		else if(va[i]>=r2) r2=va[i];
	}
	return make_pair(r1,r2);
}
inline void del(int k,int v)
{
	cnt[k]--;
	for(rrr int i=0;i<st[k].size();++i)
	{
		if(st[k][i]==v) 
		{
			st[k].erase(st[k].begin()+i);
			return;
		} 
	}
}
inline void ins(int k,int v)
{
	cnt[k]++; st[k].push_back(v);
	pair<int,int> mx=fmx(k);
	for(int i:st[k])
	{
		if(va[i]!=mx.first) a1[i]=max(a1[i],mx.first);
		else a1[i]=max(a1[i],mx.second);
		a2[i]=max(a2[i],cnt[k]-1);
	}
}
inline void write(LL x)
{
	if(!x) return;
	write(x/10);
	putchar((x%10)^48);
}
inline int read()
{
	int res=0;
	int x=0,f=1; x=getchar();
	if(x=='-') f=-1,x=getchar();
	for(;x>='0'&&x<='9';x=getchar()) res=(res*10)+x-48;
	return res*f;
}
int main()
{
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	n=read();
	for(rrr int i=1;i<=n;++i)
	{
		int x,y; 
		va[i]=read(); x=read(); y=read();
		if(!mp[x][y]) mp[x][y]=++tot;
		wh[i]=mp[x][y];
		ins(wh[i],i);
	}
	m=read();
	while(m--)
	{
		int v,x,y; 
		v=read(); x=read(); y=read();
		if(!mp[x][y]) mp[x][y]=++tot; 
		if(wh[v]==mp[x][y]) continue;
		del(wh[v],v);
		wh[v]=mp[x][y];
		ins(wh[v],v);
	}
	for(int i=1;i<=n;i++)
	{
		if(!a1[i]||!a2[i]) putchar('0');
		else write(1ll*a1[i]*a2[i]);
		putchar('\n');
	}
	return 0;
}

赛时第一次打快写,锅了。。。

正解平衡树维护最值和插入删除操作。

首先对于每一只鸟都要有一个节点记录它的信息,其次对于每一个坐标也要建一棵树维护信息。

我们采用 \(FHQ\) 就很好理解,鸟飞到一个点也就是把这两棵树合并,并分别更新鸟和当前这个点的信息。

注意因为这里不计本身,所以要先更新,然后再合并插入这只鸟。

而删除操作除了普通的 \(split\),为了精确的删除某一只鸟,我们引入位置作为限制然后劈树,和权值类似。

inline void split(int rt,int k,int &x,int &y,int p)
{
	if(!rt)  return x=y=0,void (0);
	pushdown(rt);
	if((va[rt]==k)?(rt<=p):(va[rt]<k)) x=rt,split(son[rt][1],k,son[rt][1],y,p);//pay attention
	else y=rt,split(son[rt][0],k,x,son[rt][0],p);
	pushup(rt);
}

最后就是最重要的更新操作了。

因为“威武值”和“团结值”都只取最大值,所以鸟飞走是不会有影响的,只在飞来的时候更新就行。

而我们已经把位置看成一个独立的整体,所以每次可以只对这一位置的树整体进行操作。

然而如果每一次都遍历一棵树,那和暴力没区别,因此,我们引入懒惰标记,问题就迎刃而解了。

inline void stag(int k,int x,int y)//lazy tag
{
	if(!k) return ;
	am1[k]=max(am1[k],x),am2[k]=max(am2[k],y);
	a1[k]=max(a1[k],am1[k]),a2[k]=max(a2[k],am2[k]);
}

在操作完成之后,将所有标记下放,由于每次飞走飞回的操作后都会下放,所以最后只需要最后再更新一次就好了

code
#include<bits/stdc++.h>
using namespace std;
const int N = 4e5+5;
#define LL long long
int n,t;
int va[N],son[N][2],rt[N],pri[N],a1[N],a2[N],sz[N],am1[N],am2[N];
inline void pushup(int k) {sz[k]=sz[son[k][0]]+sz[son[k][1]]+1;}
inline void stag(int k,int x,int y)
{
	if(!k) return ;
	am1[k]=max(am1[k],x),am2[k]=max(am2[k],y);
	a1[k]=max(a1[k],am1[k]),a2[k]=max(a2[k],am2[k]);
}
inline void pushdown(int k)
{
	if(!k) return;
	stag(son[k][0],am1[k],am2[k]);
	stag(son[k][1],am1[k],am2[k]);
	am1[k]=am2[k]=0;
}
inline int merge(int x,int y)
{
	if(!x||!y) return x|y;
	if(pri[x]<pri[y]) return pushdown(x),son[x][1]=merge(son[x][1],y),pushup(x),x;
	else return pushdown(y),son[y][0]=merge(x,son[y][0]),pushup(y),y;
}
inline void split(int rt,int k,int &x,int &y,int p)
{
	if(!rt)  return x=y=0,void (0);
	pushdown(rt);
	if((va[rt]==k)?(rt<=p):(va[rt]<k)) x=rt,split(son[rt][1],k,son[rt][1],y,p);
	else y=rt,split(son[rt][0],k,x,son[rt][0],p);
	pushup(rt);
}
inline void ins(int &rt,int k)
{
	int x,y;
	split(rt,va[k],x,y,k); 
	rt=merge(merge(x,k),y);
}
inline void del(int &rt,int k)
{
	int x,y,z;
	split(rt,va[k],x,y,k); split(x,va[k]-1,x,z,k-1);
	sz[k]=1; son[k][0]=son[k][1]=0; pri[k]=rand();
	rt=merge(x,y);
}
inline int mx(int rt)
{
	if(!rt) return 0;
	while(son[rt][1]) rt=son[rt][1];
	return va[rt];
}
bool vs[N];
inline void dfs(int k)
{
	if(!k) return ; vs[k]=1;
	pushdown(k); dfs(son[k][0]); dfs(son[k][1]);
}
int pos[N],cnt;
unordered_map<int,unordered_map<int,int> > mp;
inline int read()
{
	char c=getchar(); int x=0,f=1;
	while(!isdigit(c)) f=c=='-'?-1:1,c=getchar();
	while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*f;
}
inline void print(LL x)
{
	if(!x) return;
	print(x/10);
	putchar(x%10+48);
}
inline void write(LL x)
{
	if(!x) putchar('0');
	else print(x);
	putchar('\n');
}
int main()
{
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++)
	{
		va[i]=read();int x=read(),y=read();
		if(!mp[x][y]) mp[x][y]=++cnt;
		pos[i]=mp[x][y]; pri[i]=rand(); sz[i]=1;
		stag(i,mx(rt[pos[i]]),sz[rt[pos[i]]]);
		stag(rt[pos[i]],va[i],sz[rt[pos[i]]]);
		ins(rt[pos[i]],i);
	}
	t=read();
	while(t--)
	{
		int w=read(),x=read(),y=read();
		if(pos[w]==mp[x][y]) continue;
		del(rt[pos[w]],w);
		if(!mp[x][y]) mp[x][y]=++cnt;
		pos[w]=mp[x][y];
		stag(w,mx(rt[pos[w]]),sz[rt[pos[w]]]);
		stag(rt[pos[w]],va[w],sz[rt[pos[w]]]);
		ins(rt[pos[w]],w);
	}
	for(int i=1;i<=n;i++)
		if(!vs[i]) dfs(rt[pos[i]]);
	for(int i=1;i<=n;i++) write(1ll*a1[i]*a2[i]);
	return 0;
}

快读快写确实好用

标签:int,max,a1,inline,am1,山鸟飞,am2
From: https://www.cnblogs.com/ppllxx-9G/p/18292602

相关文章

  • luogu P4200 千山鸟飞绝 题解 【一维数组套平衡树】
    目录题目解题思路code题目题目链接解题思路首先,此题有明显的插入、删除、查找,所以必须要使用平衡树。考虑如何使用平衡树维护每个鸟的状态。发现很不方便,因为鸟的位置改变,整个平衡树的值都要修改。考虑针对每个节点开一颗平衡树,这样就有\(3e4\times3e4\)棵树。这显然太多了......