其他分享
首页 > 其他分享> > noip77

noip77

作者:互联网

T1

考场想法:暴力70pts好耶,然而事实上只有40pts,于是想拿70pts,找了半天规律,30mins后有了70pts,试图去写正解,因为感觉这种题大家应该都切了,于是继续耗了30mins,无果,70pts跑路。

好吧,确实是大家都切了,而我只有可怜的70pts,难受。

70pts:通过一些手段可以发现,左端点固定,那么会有一段区间答案是连续的,下一个不重复的右端点即为上一个区间答案+1,左端点的话有类似的, \(l|(l+1)\) 。

100pts:

题解写得挺详细

image

话说这种题就应该猜个结论然后走人啊...

浪费时间打表,还是菜了。

T2

考场写了个垃圾暴力,连部分分的背包都没写...

正解不会的话,还是要多考虑亿下部分分的。

30pts:普通暴力。

60pts:加个背包。

100pts:二分+单调指针乱扫。

将 \(n\) 拆成两半来写,求出前 \(\frac{n}{2}\) 和后 \(\frac{n}{2}\) 所能组成的数,然后排个序。

然后二分答案, \(check\) 时枚举在前 \(\frac{n}{2}\) 所选的数,不难发现,前半部分往后枚举,那么后半部分选的数会越来越小,所以拿个指针瞎几把扫就行。

注意对手爆蛋的情况也要算上。

Code
#include<cmath>
#include<cstdio>
#include<cctype>
#include<algorithm>
#define re register
#define int long long
const int N = 21;
#define scanf oma=scanf
using std::sort;
int oma;
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;
	double p;
	int n,w[N<<1];
	#define debug(s) debug((char*)s)
	auto debug = [](char* s) { printf("%s\n",s); };
}using namespace some;
namespace simple
{
	int ra[1<<N];
	auto solve = []() -> void
	{
		int sta = (1<<n)-1;
		for(re int i=1; i<=sta; i++)
		{
			for(re int j=1; j<=n; j++)
			{
				if((i>>j-1)&1)
				{ ra[i] += w[j]; }
			}
		}
		sort(ra+1,ra+1+sta);
		//for(re int i=0; i<=sta; i++) { printf("%d\n",ra[i]); }
		int pos = ceil((sta+1)*p)-1;
		printf("%lld\n",ra[pos]);
	};
};
namespace bag
{
	int dp[N*2000],sum,tot;
	auto solve = []() -> void
	{
		dp[0] = 1;
		for(re int i=1; i<=n; i++)
		{
			sum += w[i];
			for(re int j=sum; j>=w[i]; j--)
			{ dp[j] += dp[j-w[i]]; }
		}
		long long sta = 1ll<<n;
		long long pos = ceil(sta*p)-1;
		for(re int i=1; i<=sum; i++)
		{
			tot += dp[i];
			if(tot>=pos) { printf("%lld\n",i); return ; }
		}
	};
};
namespace right
{
	int res,lnt,rnt,level;
	int val[1<<N],var[1<<N];
	auto check = [](int mid,int pos = rnt,int sum = 0) -> bool
	{
		for(re int i=0; i<=lnt; i++)
		{
			while(pos!=-1&&val[i]+var[pos]>mid)
			{ pos--; }
			sum += pos+1;
		}
		//printf("mid=%lld sum=%lld\n",mid,sum);
		return sum>=level;
	};
	auto solve = []() -> void
	{
		int mid1 = n/2,mid2 = n/2+1;
		lnt = (1<<mid1)-1,rnt = (1<<(n-n/2))-1,level = ceil((1ll<<n)*p);
		for(re int i=1; i<=lnt; i++)
		{
			for(re int j=1; j<=mid1; j++)
			{ if((i>>j-1)&1) { val[i] += w[j]; } }
		}
		for(re int i=1; i<=rnt; i++)
		{
			for(re int j=mid2; j<=n; j++)
			{ if((i>>j-mid2)&1) { var[i] += w[j]; } }
		}
		int l = 0,r = val[lnt]+val[rnt];
		sort(val+1,val+1+lnt),sort(var+1,var+1+rnt);
		//printf("lnt=%lld rnt=%lld level=%lld\n",lnt,rnt,level);
		//for(re int i=1; i<=lnt; i++) { printf("%d ",val[i]); } printf("\n");
		//for(re int i=1; i<=rnt; i++) { printf("%d ",var[i]); } printf("\n");
		while(l<=r)
		{
			int mid = l+r>>1;
			if(check(mid))
			{ r = mid-1,res = mid; /*debug("fuck");*/ }
			else
			{ l = mid+1; }
		}
		printf("%lld\n",res);
	};
};
namespace OMA
{
	auto main = []() -> signed
	{
		freopen("answer.in","r",stdin); freopen("answer.out","w",stdout);
		cin >> n; scanf("%lf",&p);
		for(re int i=1; i<=n; i++)
		{ cin >> w[i]; }
		right::solve();
		return 0;
	};
}
signed main()
{ return OMA::main(); }

T3

考场写的还是垃圾暴力,判重用的bool开不下。

然而事实上用 \(bitset\) +大力剪枝就可以切...

一些可能没用的优化,如果想到了就加上,反正不亏。

一些STL的奇奇妙妙的东西也可以想一下。

T4

考场只写了并查集的20pts。

20pts:已述。

100pts:

如果是交集,则将新节点与集合内的点连一条边,并集则将集合内的点与新节点连一条边。

\(k=1\) 时交并等价,要建双向边。

然后直接暴力搜看是否能搜到即可ac

正解是先离线,建树求dfs序。

对于询问,如果dfs区间不存在包含关系,则答案为0.

否则的话,分情况讨论。

  1. \(x\) 是 \(y\) 的祖先, \(fa_{y}\) 到 \(x\) 都是“交” 答案为1,否则为0 。
  2. \(y\) 是 \(x\) 的祖先, \(fa_{x}\) 到 \(y\) 都是“并” 答案为1,否则为0 。

同样要特判 \(k=1\) 的情况。

以下三道为考完新加的...

T5

超级添狗题....

题解

image

T6

lun_runda题...

题解

image

T7

CF739E

标签:noip77,val,int,lnt,re,70pts,lld
来源: https://www.cnblogs.com/-OMA-/p/15420029.html