noip模拟11

A 送信卒

考场上唐了,把方向搞反导致挂零。。。

然后就是跑一边 bfs,算出来最短路,并且记录横纵位移,就好了。

好像也可以二分然后去跑 djikstra。

其实有个 hack,会让我的代码在特定情况下不稳定地输出错解:

10 10
1 1 5 5
0 0 0 0 0 0 0 0 0 0
0 1 1 1 1 1 1 1 1 0
0 1 1 1 0 0 0 0 0 0
0 1 1 1 0 1 1 1 1 1
0 1 0 0 0 1 1 1 1 1
0 1 0 1 1 1 1 1 1 1
0 1 0 1 1 1 1 1 1 1
0 1 0 1 1 1 1 1 1 1
0 1 0 1 1 1 1 1 1 1
0 0 0 1 1 1 1 1 1 1
16

B 共轭树图

首先的结论,是 \(G\) 图是一棵树。

然后省略证明,然后考虑 dp:

\(f(x,i)\) 表示以 \(x\) 为根的子树,且 \(x\) 只允许与它的第 \(i\) 个祖先在 \(G\) 连边时,子树中的方案数。考虑枚举 \(x\)\(G\) 中的父亲具体是哪一个,有转移:

\[f(x,i)=\sum_{j=1}^i \prod f(y,i-(j-1)+1) \]

然后在统计时用前缀和优化即可。

C 摸鱼军训

可算是搞懂了。

首先,我们需要确定一个数在 \(k\) 次冒泡排序后的位置,有以下几种可能:

  • \(k\) 次排序后前面仍有比他大的数;

  • 没有比他大的数

  • 他已经不会再移动(前缀已经有序)

首先,有几个比较重要的引理:

\(x\) 后方有 \(k\) 个小于它的数,第 \(k+1\) 个数大于它,那么本次移动 \(x\) 和下一个大于 \(x\) 的数中间的数都会移动到 \(x\) 前方。

证明:
我们设小于 \(x\) 的数为 \(p_1,p_2...p_k\),大于 \(x\) 的数为 \(q_1,q_2,...q_k\)
假设一次排序前的序列为 \(x,p_1,p_2,p_3,q_1,p_4,q_3\),那么显然,\(x\) 会与 \(p_1\) 交换,然后指针依然在 \(x\) 上,会与 \(p_2\) 交换,直到 \(q_1\) 为止。而接下来 \(p_4\) 会从 \(q_1\)\(q_2\) 中间移动到 \(x\)\(q_1\) 中间,以此类推。

\(x\) 前方已无大于它的数,且保证接下来的序列一定会变,那连续 \(k\) 次排序,第 \(i\) 次排序后 \(x\) 应该距离 \(p_i\) 在序列中的原位置有 \(i\) 长度。

证明:
我们已知一次排序后 \(x\) 会处在某个大于它的数的前面,那么根据上面的结论,第 \(i\) 次排序会使 \(q_{i-1}\)\(q_i\) 中间的所有 \(p\) 移动到 \(q_{i-2}\)\(q_{i-1}\) 中间,
此时,我们假设序列中没有 \(<x\) 的数,那排序到 \(k\) 次时确实距离 \(p_k\)\(i\)
那现在有了 \(<x\) 的数,原序列中 \(p_k\) 前面的、\(<x\) 的数来到了 \(x\) 的前方,假设有 \(m\) 个,
我们不关心 \(p_k\) 后方的数产生的影响,因为在 \(k\) 次后它们到不了 \(x\) 的前面,即对 \(x\) 现在的位置没有影响。
假设原序列中 \(x\)\(p_k\) 的距离为 \(k+m\),即有 \(k\) 个大于 \(x\) 的,\(m\) 个小于 \(x\) 的,
那么既然那 \(m\)\(<x\) 的数已然来到 \(x\) 前方,所以 \(x\)\(q_k\) 的原位置不就是 \(k+m-m\),那不就是 \(k\) 吗。

情况 \(1\)

\(x\) 前面仍有大于它的数,那距离 \(x\) 最近的、大于 \(x\) 的数一定会移动到 \(x\) 后方。证明可看引理 1

那么,有 \(i\) 个大于 \(x\) 的数,就一定需要 \(i\) 轮去把它们移动到 \(x\) 后方。并且,每移动一轮,\(x\) 的位置都会 \(-1\)

因为有一个在它前面的大于它的数到了后面,且只有一个,所以会 \(-1\)

记原序列中在位置 \(p\) 前方且大于 \(a_p\) 的数的个数为 \(rev_p\),那么对于所有 \(k\le rev_p\) 的轮,\(a_p\) 的位置一定是 \(p-k\)

情况 \(2\):

此时,\(x\) 的前方已经没有比它大的数了,那我们根据引理 2,在第 \(k\) 轮,\(x\)\(p_k\) 原位置的距离为 \(k\),只需要维护所有大于 \(x\) 的数的位置,并且找第 \(k\) 大的,那么本次排序后 \(x\) 的位置就是 \(pos_{p_k}-k\) 了。

情况 \(3\)

在第 \(i\) 轮,编号为 \(n-i+1\) 的数一定会被移动到最终位置。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
const int N=5e5+5;
inline int read()
{
	register int s=0;
	register char c=getchar();
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') s=(s<<1)+(s<<3)+(c^48),c=getchar();
	return s;
}
void write(int x)
{
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
int q,a[N];
struct OP{
	int k,x,id;
	inline bool operator<(const OP &ll) const {
		return x>ll.x;
	}
}op[N];
int ans[N],pos[N];
struct BIT{
	int c[N];
	inline int lowbit(int x)
	{
		return x&-x;
	}
	void update(int x)
	{
		for(;x<=n;x+=lowbit(x)) c[x]++;
	}
	int query(int x)
	{
		int res=0;
		for(;x;x-=lowbit(x)) res+=c[x];
		return res;
	}
}st1;
int rev[N];
struct SegTree{
	struct{
		int sum;
	}tr[N<<2];
	#define lid now<<1
	#define rid now<<1|1
	void update(int now,int l,int r,int x,int y)
	{
		if(x<=l&&r<=y)
		{
			tr[now].sum++;return  ;
		}int mid=(l+r)>>1;
		if(x<=mid) update(lid,l,mid,x,y);
		if(y>mid) update(rid,mid+1,r,x,y);
		tr[now].sum=tr[lid].sum+tr[rid].sum;
	}
	int query(int now,int l,int r,int k)
	{
		if(l==r) return l;
		int mid=(l+r)>>1;
		if(tr[lid].sum>=k) return query(lid,l,mid,k);
		else return query(rid,mid+1,r,k-tr[lid].sum);
	}
}st;
signed main()
{
	freopen("bubble.in","r",stdin);
	freopen("bubble.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++) a[i]=read(),pos[a[i]]=i;
	q=read();
	for(int i=1;i<=n;i++)
	{
		rev[i]=st1.query(n)-st1.query(a[i]);
		st1.update(a[i]);
	}
	int mxk=0,tot=0;
	for(int i=1;i<=q;i++)
	{
		int kk=read(),xx=read();
		int p=pos[xx];
		if(rev[p]>=kk) ans[i]=p-kk;
		else if(n<xx+kk) ans[i]=xx;
		else
		{
			op[++tot].k=kk,op[tot].x=xx,op[tot].id=i;
		}
	}
	sort(op+1,op+1+tot);
	int now=n+1;
	for(int i=1;i<=tot;i++)
	{
		while(now>1&&now-1>op[i].x)
		{
			now--;
			st.update(1,1,n,pos[now],pos[now]);
		}
		ans[op[i].id]=st.query(1,1,n,op[i].k)-op[i].k;
	}
	for(int i=1;i<=q;i++) cout<<ans[i]<<"\n";
	return 0;
}

D 神奇园艺师

posted @ 2024-11-12 20:53  ccjjxx  阅读(24)  评论(1)    收藏  举报