noip模拟34 Merchant Equation Rectangle
好像又咕了好久的样子 \((\)\(\color{white}{{7)\lbrace}}\)
再来写一篇(
这次考得感觉是过去几天最好的一场(
考场上顺序开题。
\(\mathrm{A.}\mathbb{Merchant}\):不知道
\(\mathrm{B.}\mathbb{Equation}\):数据结构
\(\mathrm{C.}\mathbb{Rectangle}\):不知道
开始时,先把三道题都看了一遍,觉得没一道可做的QAQ
\(\mathrm{A}\) 和 \(\mathrm{C}\) 暂时没有什么好的思路,看到 \(\mathrm{B}\) 时:
内心OS:这不是以 \(\mathrm{dfs}\) 序为下标,建一棵线段树维护一堆东西的大水题嘛!
其实不是这样的。
某位julao曾经说过:如果你打算在考场上写 \(\mathrm{DS}\) 的话,就要承受抱灵的风险。
于是我就用了两个多小时连写带调带对拍,写完了 \(\mathrm{B}\)。
用最后的时间,写了道 \(\mathrm{A}\) 的暴力。
结果出分后,因为 \(\mathrm{A}\) 的傻逼错误,挂了 \(8\) 分,\(\mathrm{B}\) 题人均 \(86\)。
估分:\(8+100+0=108\)
实际:\(0+86+0=86\)
还是说正解吧(
A.Merchant
这题考场压根没看,最后只打了个 \(8\) 分的特判,结果还挂了。
正解 \(\color{white}{\mathrm{-7}}\)
设 \(f_i(t)=k_i\times t+b_i\),容易发现他们的最大值,即 \(\displaystyle f_{max}(t)=\max_{1\le i\le n}f_i(t)\) 是先减后增的。证明很容易,当某一个时刻 \(t^\prime\) 满足 \(f_{max}(t^\prime)>f_{max}(t^\prime-1)\)时,\(f_{max}\) 的斜率必为正,因此 \(f_{max}(t^\prime+1)>f_{max}(t^\prime)\),可以得出当 \(f_{max}\) 开始递增时,就不会再递减了。
知道了这个性质,我们就可以用二分来检验时刻 \(t^\prime\) 是否能作为答案,具体过程如下:
先判断 \(0\) 能否为最终答案,再进行二分,每次算出所有的 \(f_i\) 求出前 \(m\) 大的,若 \(f_i\) 为正,则加上,否则不加,判断是否大于 \(S\),求前 \(m\) 大可以用 \(\mathrm{nth\_element}\) 来实现。
总复杂度为 \(O(n\log 10^9)\)
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,m;
long long slim;
long long k[N],b[N];
long long s[N],sum;
bool check(int t)
{
	sum=0;
	for(int i=1;i<=n;i++) s[i]=b[i]+k[i]*t;
	nth_element(s+1,s+n-m,s+n+1);
	for(int i=n-m+1;i<=n;i++)
	{
		if(s[i]<=0) continue;
		sum+=s[i];
		if(sum>=slim) return true;
	}
	return sum>=slim;
}
int main()
{
	scanf("%d%d%lld",&n,&m,&slim);
	for(int i=1;i<=n;i++) scanf("%lld%lld",&k[i],&b[i]);
	if(check(0))
	{
		printf("0\n");
		return 0;
	}
	int l=1,r=1e9+5;
	while(l<r)
	{
		int mid=(l+r)>>1;
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	printf("%d\n",l);
	return 0;
}
B.Equation
考场上发现这道题会做异常兴奋(
就是。。。
点名卡常线段树(QAQ
正解 \(\color{white}{\mathrm{-6}}\)
容易发现每一个点都能够表示成 \(s+x_1\) 或 \(s-x_1\) 的形式,并且正负与每个点深度的奇偶性相关。
对于查询操作,对 \(u\) 和 \(v\) 进行单点查询,若深度的奇偶性不同,则只有无解或无数组解两种可能,若奇偶性相同则需判断解出的 \(x_1\) 是否是 \(2\) 的倍数。
对于修改操作,若深度为奇数,子树区间 \(+w\),否则 \(-w\)。
时间复杂度为 \(O(q\log n)\),然而这道题卡线段树,只能拿树状数组写。
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,q;
int f[N],w[N];
int cnt,head[N];
int p,id[N],dep[N],size[N];
long long tree[N];
struct Node
{
	int to,nxt;
}e[N<<1];
void adde(int u,int v)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
void add(int x,int y)
{
    for(;x<=n;x+=x&-x) tree[x]+=y;
}
int ask(int x)
{
    int ans=0;
    for(;x;x-=x&-x) ans+=tree[x];
    return ans;
}
void dfs(int u)
{
	id[u]=++p;
	size[u]=1;
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==f[u]) continue;
		dep[v]=dep[u]+1;
		dfs(v);
		size[u]+=size[v];
	}
}
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-48;
		ch=getchar();
	}
	return x*f;
}
int main()
{
	n=read();
	q=read();
	for(int i=2;i<=n;i++)
	{
		f[i]=read();
		w[i]=read();
		adde(i,f[i]);
		adde(f[i],i);
	}
	dep[1]=1;
	dfs(1);
	for(int u=2;u<=n;u++)
	{
		if(dep[u]&1)
		{
			add(id[u],w[u]);
			add(id[u]+size[u],-w[u]);
		}
		else
		{
			add(id[u],-w[u]);
			add(id[u]+size[u],w[u]);
		}
	}
	while(q--)
	{
		int opt=read();
		if(opt==1)
		{
			int u=read(),v=read();
			long long s=read();
			long long xu=ask(id[u]),xv=ask(id[v]);
			if(!(dep[u]&1)) xu=-xu;
			if(!(dep[v]&1)) xv=-xv;
			if((dep[u]+dep[v])&1) xu+xv==s?printf("inf\n"):printf("none\n");
			else if(!(abs(xu+xv-s)&1))
			{
				if(dep[u]&1) printf("%d\n",(s-xu-xv)>>1);
				else printf("%d\n",(xu+xv-s)>>1);
			}
			else printf("none\n");
		}
		else
		{
			int u=read();
			long long dis=read();
			long long t=dis;
			dis-=w[u];
			w[u]=t;
			if(dep[u]&1)
			{
				add(id[u],dis);
				add(id[u]+size[u],-dis);
			}
			else
			{
				add(id[u],-dis);
				add(id[u]+size[u],dis);
			}
		}
	}
	return 0;
}
C.Rectangle
正解 \(\color{white}{\mathrm{-6}}\)
五篇好题解 \(\color{white}{\mathrm{-9}}\)
由柿子可以得出,对于一对 \(l,r\),只需记录它点的个数与点的纵坐标和,因此可以用树状数组维护,对于有多个点的情况,可以将其纵向分割成几个部分,分别计算贡献即可。
时间复杂度:\(O(2500n\log 2500)\)
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e4+5,XY=2500,mod=1e9+7;
int n;
int a[N][N];
long long tree[XY+5][2];
bool vis[XY+5][XY+5];
long long ans;
void add(int x,int opt,int y)
{
    for(;x<=XY;x+=x&-x) tree[x][opt]+=y;
}
int ask(int x,int opt)
{
    int ans=0;
    for(;x;x-=x&-x) ans+=tree[x][opt];
    return ans;
}
signed main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		int x,y;
		scanf("%lld%lld",&x,&y);
		a[x][++a[x][0]]=y;
	}
	for(int i=1;i<=XY;i++)
	{
		sort(a[i]+1,a[i]+a[i][0]+1);
		a[i][a[i][0]+1]=XY+1;
	}
	for(int i=1;i<=XY;i++)
	{
		memset(tree,0,sizeof(tree));
		if(!a[i][0]) continue;
		for(int j=1;j<=a[i][0];j++)
		{
			if(vis[i][a[i][j]]) continue;
			vis[i][a[i][j]]=1;
			add(a[i][j],0,1);
			add(a[i][j],1,a[i][j]);
		}
		for(int j=i-1;j>=1;j--)
		{
			if(!a[j][0]) continue;
			for(int k=1;k<=a[j][0];k++)
			{
				if(vis[i][a[j][k]]) continue;
				vis[i][a[j][k]]=1;
				add(a[j][k],0,1);
				add(a[j][k],1,a[j][k]);
			}
			int pi=1,pj=1,now=max(a[i][1],a[j][1]);
			while(a[i][pi+1]<=now) pi++;
			while(a[j][pj+1]<=now) pj++;
			while(pi<=a[i][0]&&pj<=a[j][0])
			{
				int up=min(a[i][pi+1],a[j][pj+1]),down=min(a[i][pi],a[j][pj]);
				ans=(ans+(i-j)*((ask(up-1,1)-ask(now-1,1))*ask(down,0)-(ask(up-1,0)-ask(now-1,0))*ask(down,1)))%mod;
				now=up;
				if(a[i][pi+1]<=now) pi++;
				if(a[j][pj+1]<=now) pj++;
			}
		}
	}
	printf("%lld\n",ans);
	return 0;
}
\(\color{white}\rbrace\)

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号