其他分享
首页 > 其他分享> > (联考)noip91

(联考)noip91

作者:互联网

T1

不难发现,答案就是第一类斯特林数。

递推公式:

\[\left[\begin{array}{l} n \\ k \end{array}\right]=\left[\begin{array}{l} n-1 \\ k-1 \end{array}\right]+(n-1)\left[\begin{array}{c} n-1 \\ k \end{array}\right] \]

不过考场上并不知道这玩意叫第一类斯特林数

T2

总区间个数为 \(\frac{n(n+1)}{2}\) 。

发现如果一个区间 \([l,r]\) 要产生贡献,则 \(s_{l}\neq s_{r}\) ,否则去掉两端的字符后再翻转,得到的字符串是一样的,这样的话就在总区间个数里算重了,需要减去。

所以直接扫一遍,每回减去当前位置上的字符之前出现的次数,这样没有算到翻转字符串长度为1的情况,最后再单独计算即可。

T3

同类合并显然,然后状压即可。

T4

考场上连二分答案都没有想到...

显然要先将影分身和卷轴排好序。

答案具有单调性,如果当前时间合法,那么比它大的肯定也合法。

问题在于如何 \(O(n)\) check。

枚举影分身,拿个指针去扫卷轴,对于当前扫到的卷轴有两种情况:

  1. 卷轴在影分身的右侧,那就直接往右走,如果不能走到,应去枚举下一个影分身,看是否能让它拾取此卷轴。

  2. 卷轴在影分身的左侧,要保证其能在拾取到上一个没能拾取到的最靠右的卷轴的前提下,尽可能的往右走。这样的话就可以先往左后往右,也可以先往右后往左。如果无法走到,那么下一个肯定也无法走到,直接return即可。

一些细节见code。

Code
#include<cmath>
#include<cstdio>
#include<cctype>
#include<algorithm>
using std::sort;
#define re register
const int MAX = 3e5+3;
const int INF = 1.4e9+3;
#define long long long
#define scanf oma=scanf
#define freopen file=freopen
int oma;
FILE* file;
namespace some
{
	struct stream
	{
		template<typename type>inline stream &operator >>(type &s)
		{
			bool w=0; s=0; char ch=getchar();
			while(!isdigit(ch)){ w|=ch=='-'; ch=getchar(); }
			while(isdigit(ch)){ s=(s<<1)+(s<<3)+(ch^48); ch=getchar(); }
			return s=w?-s:s,*this;
		}
	}cin;
	int n,m,res;
	int p1[MAX],p2[MAX];
	auto max = [](int a,int b) { return a>b?a:b; };
	#define debug(s) printf("%s\n",s)
}using namespace some;
namespace OMA
{
	auto check = [](int mid,int p = 0) -> bool
	{
		//if(mid==38) { printf("mid=%d\n",mid); }
		for(re int i=1,rec; i<=n; i++)
		{
			p++;
			//if(mid==38) { printf("now=%d p=%d\n",i,p); }
			if(p1[i]<p2[p])
			{
				rec = p1[i]+mid;
				if(rec<p2[p])
				{
					p--; // 如果当前无法拾取到,考虑看是否能让下一个拾取到,因为循环前有p++的操作,所以此处需减去当前影分身的+1
					//if(mid==38) { printf("???: i=%d p=%d\n",i,p); }
					//return 0;
				}
			}
			else if(p1[i]>p2[p])
			{
				if(p1[i]-p2[p]>mid)
				{
					//if(mid==38) { printf("i=%d\n",i); debug("shit"); }
					return 0; // 当前的无法拾取到其左边的卷轴,那么下一个也一定无法拾取到,所以直接return即可。
				}
				rec = max(mid-p1[i]+p2[p]*2,(mid+p1[i]+p2[p])/2);
			}
			//if(mid==38) { printf("before: p=%d i=%d to=%d\n",p,i,rec); }
			while(p<m&&p2[p+1]<=rec)
			{ p++; }
			if(p==m)
			{
				//if(mid==38) { printf("mid=%d i=%d to=%d\n",mid,i,rec); }
				return 1;
			}
			//if(mid==38) { printf("p=%d i=%d cmp=%d to=%d\n",p,i,p2[p],rec); }
		}
		//printf("p=%d\n",p);
		//if(mid==38) { debug("shit"); }
		return 0;
	};
	auto main = []() -> signed
	{
		//#define local
		#ifdef local
		//debug("look here! if you want submit,please closed this");
		freopen("node.in","r",stdin); //freopen("my.out","w",stdout);
		#endif
		freopen("duplication.in","r",stdin); freopen("duplication.out","w",stdout);
		cin >> n >> m;
		for(re int i=1; i<=n; i++)
		{ cin >> p1[i]; }
		for(re int i=1; i<=m; i++)
		{ cin >> p2[i]; }
		sort(p1+1,p1+1+n),sort(p2+1,p2+1+m);
		//for(re int i=1; i<=n; i++) { printf("%d ",p1[i]); } debug("");
		//for(re int i=1; i<=m; i++) { printf("%d ",p2[i]); } debug("");
		//printf("%d\n",p2[m]);
		//check(119);
		int l = 1,r = INF;
		while(l<=r)
		{
			int mid = l+r>>1;
			//printf("l=%d mid=%d r=%d\n",l,mid,r);
			if(check(mid))
			{ r = mid-1,res = mid; }
			else
			{ l = mid+1; }
		}
		printf("%d\n",res);
		return 0;
	};
}
signed main()
{ return OMA::main(); }

标签:int,noip91,mid,卷轴,freopen,printf,array,联考
来源: https://www.cnblogs.com/-OMA-/p/15522610.html