ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[省选集训2022] 模拟赛2

2022-01-23 23:00:19  阅读:123  来源: 互联网

标签:... frac 省选 mid int 2022 return include 集训


A

题目描述

有 \(n\) 个在 \([0,2^w)\) 内的非负整数,你需要执行下面的操作 \(n-1\) 次,使得剩下的数最小:

  • 选择两个非负整数 \(x,y\),将其合并成一个非负整数 \(z\),其中 \(z=\lfloor\frac{(x|y)}{2}\rfloor\)
  • 选择一个数 \(x\) 将其删去。

\(n\leq 10^5,w\leq 60\)

解法

可以把第一步操作拆分:\(\lfloor\frac{(x|y)}{2}\rfloor=\lfloor\frac{x}{2}\rfloor|\lfloor\frac{y}{2}\rfloor\),对于具有复杂过程的题目可以考虑向结果的方向猜结论,就像 To make one 这题一样,设 \(d_i\) 表示最后第 \(i\) 个数被除二的次数,那么最后的答案可以表示成 \(\frac{a_1}{2^{d_1}}|\frac{a_2}{2^{d_2}}...|\frac{a_n}{2^{d_n}}\)

结合删除操作考虑,一组 \(\{d\}\) 合法的充要条件是 \(\sum_{i=1}^n\frac{1}{2^{d_i}}\geq 1\)

有了这个结论我们从高位到低位考虑,假设现在考虑到了数位 \(w\),对于每个数我们维护 \(d_i\),然后考虑把答案的第 \(w\) 位变成 \(0\) 看是否可行,那么现在要验证的答案是 11010(0)??...,由于或操作我们不能出现答案是 \(0\) 但是第 \(i\) 个数右移 \(d_i\) 之后的对应位是 \(1\)(可以先或再异或来直接判断),我们要让 \(d_i\) 尽可能小,不合法我们再一步一步增加。

时间复杂度 \(O(nw^2)\),但是非常不满所以可以随便跑。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 100005;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int T,n,w,ans,a[M],d[M],td[M];
void work()
{
	n=read();w=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	ans=(1ll<<w)-1;
	memset(d,0,sizeof d);
	for(int t=w-1;t>=0;t--)
	{
		int now=ans^(1ll<<t),sum=0;
		for(int i=1;i<=n;i++)
		{
			td[i]=d[i];
			while(((a[i]>>d[i])|now)^now) d[i]++;
			sum+=(1ll<<w-d[i]);
			if(sum>=(1ll<<w)) break; 
		}
		if(sum>=(1ll<<w)) ans=now;
		else memcpy(d,td,sizeof td);
	}
	printf("%lld\n",ans);
}
signed main()
{
	freopen("merge.in","r",stdin);
	freopen("merge.out","w",stdout);
	T=read();
	while(T--) work();
}

B

题目描述

对于字符串 \(T\),定义一次行走位选择一个任意长度的正整数序列 \(t_1,t_2...t_m\),其中 \(\forall i\in[1,m],t_i\in [1,|T|],|t_i-t_{i-1}|=1,t_1\not=t_m\),并且 \(T_{t_1},T_{t_2}...T_{t_m}\) 是一个回文串。

称一个字符串是好的当且仅当可以通过若干次行走使得这个字符串的每个位置被经过至少一次。现在有 \(q\) 次询问,每次问一个子串 \(s[l_i...r_i]\) 是不是好的。

\(n,q\leq 10^6\)

解法

手玩样例可以发现:如果存在 ABA 这样的小奇回文串,那么一定存在解。这是因为从一个点访问所有点再回到这个点可以构成回文串,但是不满足首尾下标不同的限制,而这种情况可以让我们最后走到另一个 A 得到解。

现在的问题是:考察偶回文串是否能覆盖完整个区间,我们考虑把这个限制分解到每个位置上,设某个点向左的最近对称点是 \(l_i\),向右的最近对称点是 \(r_i\),我用如下的图做进一步解释:

上图表示的是不合法的点 \(i\),那么一个询问不能被偶回文串覆盖的充要条件是 \(\exist i\in[L,R],[L,R]\subseteq (l_i,r_i)\),其中 \(l_i,r_i\) 可以在一开始用二分哈希\(+\)线段树简单预处理出来,特别地,如果左边没有覆盖它对称中心那么 \(l_i=0\),如果右边没有覆盖它的对称中心那么 \(r_i=n+1\)

那么剩下的就是一个偏序问题了,我们考虑按左端点的顺序做扫描线,扫描的过程是这样的:

  • 加入所有的 \(l_i<L\),这样我们解决了四分之一的偏序关系。
  • 如果右端点落在 \([i,r_i)\) 中一定不合法,那么我们在这个区间中打个不合法标记。
  • 最后还需要解决 \(i\geq L\),所以我们在查询前需要删除 \(i<L\) 的点。

那么时间复杂度 \(O(n\log n)\),简单指针加树状数组做一下即可。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <set>
using namespace std;
const int M = 1000005;
#define pii pair<int,int>
#define ull unsigned long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,a[M],b[M],id[M],ans[M],mx[M<<2],mi[M<<2];
ull pw[M],hs[M][2];char s[M];multiset<pii> z;
struct node
{
	int l,r,id;
	bool operator < (const node &b) const
		{return l<b.l;}
}h[M],q[M];
//segment tree
ull check(int l,int r,int op)
{
	if(op==0) return hs[r][0]-hs[l-1][0]*pw[r-l+1];
	return hs[l][1]-hs[r+1][1]*pw[r-l+1];
}
void updmax(int i,int l,int r,int L,int R,int c)
{
	if(L>r || l>R) return ;
	if(L<=l && r<=R) {mx[i]=max(mx[i],c);return ;}
	int mid=(l+r)>>1;
	updmax(i<<1,l,mid,L,R,c);
	updmax(i<<1|1,mid+1,r,L,R,c);
}
void updmin(int i,int l,int r,int L,int R,int c)
{
	if(L>r || l>R) return ;
	if(L<=l && r<=R) {mi[i]=min(mi[i],c);return ;}
	int mid=(l+r)>>1;
	updmin(i<<1,l,mid,L,R,c);
	updmin(i<<1|1,mid+1,r,L,R,c);
}
void dfs(int i,int l,int r)
{
	if(l==r) {id[l]=i;return ;}
	int mid=(l+r)>>1;
	mx[i<<1]=max(mx[i<<1],mx[i]);
	mx[i<<1|1]=max(mx[i<<1|1],mx[i]);
	mi[i<<1]=min(mi[i<<1],mi[i]);
	mi[i<<1|1]=min(mi[i<<1|1],mi[i]);
	dfs(i<<1,l,mid);
	dfs(i<<1|1,mid+1,r);
}
void init()
{
	pw[0]=1;
	for(int i=1;i<=n;i++)
	{
		if(i+2<=n && s[i]==s[i+2]) a[i]++;
		pw[i]=pw[i-1]*371;a[i]+=a[i-1]; 
		hs[i][0]=hs[i-1][0]*371+s[i];
	}
	for(int i=n;i>=1;i--)
		hs[i][1]=hs[i+1][1]*371+s[i];
	for(int i=1;i<=4*n;i++) mi[i]=n+1;
	for(int i=1;i<n;i++)
	{
		int l=1,r=min(i,n-i),len=0;
		while(l<=r)
		{
			int mid=(l+r)>>1;
			if(check(i-mid+1,i,0)==check(i+1,i+mid,1))
				len=mid,l=mid+1;
			else r=mid-1;
		}
		if(len)
			updmax(1,1,n,i+1,i+len,i+1),
			updmin(1,1,n,i-len+1,i,i);
	}
	dfs(1,1,n);
	for(int i=1;i<=n;i++)
	{
		int t1=mx[id[i]],t2=mi[id[i]];
		h[i]=node{!t1?0:2*t1-i-1,t2>n?n+1:2*t2-i+1,i};
	}
}
//tree-array
int lowbit(int x)
{
	return x&(-x);
}
void add(int x,int c)
{
	for(int i=x;i<=n;i+=lowbit(i))
		b[i]+=c;
}
int ask(int x)
{
	int r=0;
	for(int i=x;i>0;i-=lowbit(i))
		r+=b[i];
	return r;
}
signed main()
{
	freopen("pass.in","r",stdin);
	freopen("pass.out","w",stdout);
	n=read();scanf("%s",s+1);
	init();m=read();
	for(int i=1;i<=m;i++)
	{
		q[i].l=read(),q[i].r=read(),q[i].id=i;
		if(q[i].r>2) ans[i]|=(a[q[i].r-2]-a[q[i].l-1]>0);
	}
	sort(h+1,h+1+n);sort(q+1,q+1+m);
	for(int i=1,j=1;i<=m;i++)
	{
		while(j<=n && h[j].l<q[i].l)
		{
			add(h[j].id,1);add(h[j].r,-1);
			z.insert(make_pair(h[j].id,h[j].r));
			j++;
		}
		while(!z.empty() && z.begin()->first<q[i].l)
		{
			pii t=*z.begin();z.erase(z.begin());
			add(t.first,-1);add(t.second,1);
		}
		ans[q[i].id]|=(ask(q[i].r)==0);
	}
	for(int i=1;i<=m;i++)
		putchar(ans[i]+'0');
}

标签:...,frac,省选,mid,int,2022,return,include,集训
来源: https://www.cnblogs.com/C202044zxy/p/15837755.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有