Codeforces Round 932 (Div. 2)

Preface

这周开始晚课变得巨多,导致基本没有时间自己写题

眼看着这周五周六又有CF,赶紧把上周五的这场补了

这场感觉C有点诈骗了,我反正是去写线段树上二分了,不带$\log $的做法还要动脑


A. Entertainment in MAC

签到,可能的串就两种,要么保留原串;要么reverse后再拼接原串

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
int t,n; string a;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	ios::sync_with_stdio(false); cin.tie(0);
	for (cin>>t;t;--t)
	{
		cin>>n>>a; string tmp=a;
		reverse(tmp.begin(),tmp.end()); string b=tmp+a;
		cout<<min(a,b)<<endl;
	}
	return 0;
}

B. Informatics in MAC

首先不难发现如果分多个区间合法,那么合并其中若干个区间后亦然合法,因此只要考虑分成两个区间的情况

从小到大枚举每个数,用它在原序列中出现的位置来更新分界点的可能取值,当出现某个未出现的数时则找到解

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=100005;
int t,n,a[N],l[N],r[N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d",&n),i=0;i<=n;++i) l[i]=n+1,r[i]=0;
		for (i=1;i<=n;++i) scanf("%d",&a[i]),l[a[i]]=min(l[a[i]],i),r[a[i]]=max(r[a[i]],i);
		int L=1,R=n-1; bool flag=0;
		for (i=0;i<=n;++i)
		{
			if (l[i]>n)
			{
				flag=1; printf("2\n%d %d\n%d %d\n",1,L,L+1,n); break;
			}
			L=max(L,l[i]); R=min(R,r[i]-1);
			if (L>R) break;
		}
		if (!flag) puts("-1");
	}
	return 0;
}

C. Messenger in MAC

首先一眼将二元组按照\(b_i\)排序,考虑当我们确定选取的下标位于区间\({L,R}\)时,后面的贡献总是\(b_R-b_L\)

由于本题数据范围允许\(O(n^2)\)枚举区间,因此确定端点后就是要在\([L+1,R-1]\)中找出最多数量的数使得它们的和小于某个值了

用权值线段树随便处理一下,询问的话在线段树上二分,总复杂度\(O(n^2\log n)\)

(PS:好像有基于单调性的\(O(n^2)\)DP做法,但懒得管了能过就行)

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define int long long
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=2005;
int t,n,m; pi p[N]; vector <int> rst;
class Segment_Tree
{
	private:
		int sz[N<<2],sum[N<<2];
	public:
		#define TN CI now=1,CI l=0,CI r=rst.size()-1
		#define LS now<<1,l,mid
		#define RS now<<1|1,mid+1,r
		inline void init(TN)
		{
			sz[now]=sum[now]=0; if (l==r) return; int mid=l+r>>1; init(LS); init(RS);
		}
		inline void insert(CI pos,TN)
		{
			++sz[now]; sum[now]+=rst[pos]; if (l==r) return; int mid=l+r>>1;
			if (pos<=mid) insert(pos,LS); else insert(pos,RS);
		}
		inline int query(CI lim,TN)
		{
			if (l==r) return min(sz[now],lim/rst[l]); int mid=l+r>>1;
			if (lim<=sum[now<<1]) return query(lim,LS); else return sz[now<<1]+query(lim-sum[now<<1],RS);
		}
		#undef TN
		#undef LS
		#undef RS
}SEG;
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%lld",&t);t;--t)
	{
		RI i,j; rst.clear(); int ans=0;
		for (scanf("%lld%lld",&n,&m),i=1;i<=n;++i)
		scanf("%lld%lld",&p[i].se,&p[i].fi),rst.push_back(p[i].se);
		sort(rst.begin(),rst.end()); rst.erase(unique(rst.begin(),rst.end()),rst.end());
		for (i=1;i<=n;++i) if (p[i].se<=m) ans=1;
		for (sort(p+1,p+n+1),i=1;i<n;++i)
		{
			if (p[i+1].fi-p[i].fi+p[i].se+p[i+1].se<=m) ans=max(ans,2LL);
			for (SEG.init(),j=i+1;j<n;++j)
			{
				SEG.insert(lower_bound(rst.begin(),rst.end(),p[j].se)-rst.begin());
				int tmp=p[j+1].fi-p[i].fi+p[i].se+p[j+1].se;
				if (tmp<=m) ans=max(ans,2LL+SEG.query(m-tmp));
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

D. Exam in MAC

刚开始读假题了感觉一点不可做,后面发现原来是又犯病了

考虑用容斥,所有二元组的总数为\(\frac{(c+1)\times (c+2)}{2}\),对于每个给定的\(s_i\),分别考虑\(y-x=s_i\)\(y+x=s_i\)的二元组\((x,y)\)数量,显然前者有\(c+1-s_i\)个,后者有\(\lfloor\frac{s_i}{2}\rfloor +1\)

现在考虑要加回那些被统计了两次的二元组数量,设存在二元组\((x,y)\)被统计了两次,即\(M=y-x,N=y+x\)均在\(\{s_i\}\)中出现

注意到\(x=\frac{N-M}{2},y=\frac{N+M}{2}\),因此当\(M,N\)的奇偶性相同时,必然存在一组合法且唯一的\((x,y)\)与之对应

因此统计出\(\{s_i\}\)中奇/偶数个数\(odd,even\),答案再加上\(\frac{odd\times (odd+1)}{2}+\frac{even\times (even+1)}{2}\)即可

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define int long long
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
int t,n,c,x;
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%lld",&t);t;--t)
	{
		RI i; int cnt[2]={0,0}; int dec=0;
		for (scanf("%lld%lld",&n,&c),i=1;i<=n;++i)
		scanf("%lld",&x),++cnt[x&1],dec+=(c+1-x)+(x/2+1);
		printf("%lld\n",(c+2)*(c+1)/2LL-dec+(cnt[0]+1)*cnt[0]/2LL+(cnt[1]+1)*cnt[1]/2LL);
	}
	return 0;
}

E. Distance Learning Courses in MAC

经典二进制+DS的题,为什么感觉这类题目就是出不完呢

首先考虑如果每个数选择没有下界怎么做,不妨考虑二进制下每一位在区间中出现的次数

显然如果出现\(0/1\)次那么这位的取值肯定是确定的,如果出现\(>1\)次则必然可以通过将其中一个数这一位改为\(0\),然后将小于该位的全部取为\(0\),这样一定是最优的

现在考虑加上下界的限制该怎么做,不难发现这其实就是限制了某些位是不能用上述的方式修改的

具体地,我们找到\(x,y\)二进制下最高且不同的位,则对于这个数来说,这一段前缀的值就固定了,而后面部分可以随便取

因此把每个数固定的部分拆出来后,剩下的部分就转化为没有下界的问题了,可以从高位到低位贪心处理

注意到或运算允许查询的时候区间有重叠,因此可以直接拿个RMQ维护下固定部分的贡献,而每一位出现的次数可以用前缀和处理

总复杂度\(O((n+q)\log n)\)

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=200005;
int t,n,q,x,y,pfx[N][30];
namespace RMQ
{
	int f[N][20],_log[N];
	inline void init(void)
	{
		RI i,j; for (_log[0]=-1,i=1;i<=n;++i) _log[i]=_log[i>>1]+1;
		for (j=1;(1<<j)<=n;++j) for (i=1;i+(1<<j)-1<=n;++i)
		f[i][j]=f[i][j-1]|f[i+(1<<j-1)][j-1];
	}
	inline int query(CI l,CI r)
	{
		int k=_log[r-l+1]; return f[l][k]|f[r-(1<<k)+1][k];
	}
};
using namespace RMQ;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%lld",&t);t;--t)
	{
		RI i,j; for (scanf("%d",&n),i=1;i<=n;++i)
		{
			scanf("%d%d",&x,&y);
			if (x==y) f[i][0]=x,y=0; else
			{
				int k=__lg(x^y); f[i][0]=y;
				y&=((1<<k+1)-1); f[i][0]^=y;
			}
			for (j=0;j<30;++j) pfx[i][j]=pfx[i-1][j]+((y>>j)&1);
		}
		for (init(),scanf("%d",&q),i=1;i<=q;++i)
		{
			scanf("%d%d",&x,&y); int ans=0,res=query(x,y);
			for (j=29;j>=0;--j)
			{
				int bits=pfx[y][j]-pfx[x-1][j]+((res>>j)&1);
				if (bits>1) { ans|=((1<<j+1)-1); break; }
				else if (bits==1) ans|=(1<<j);
			}
			printf("%d%c",ans," \n"[i==q]);
		}
	}
	return 0;
}

Postscript

最近事情好多的说……

posted @ 2024-03-13 16:57  空気力学の詩  阅读(17)  评论(0编辑  收藏  举报