【CF套题】Codeforces Round #524 (Div. 2) (1080A~F)

版权声明:这是蒟蒻的BLOG,神犇转载也要吱一声哦~ https://blog.csdn.net/Dream_Lolita/article/details/84564756

原题地址
回归 CF \text {CF} ,这场堪堪上紫。

A.Petya and Origami

【题目大意】
一本笔记本有 k k 页,每个笔记本只能染成红绿蓝其中一种颜色,需要 2 n 2n 页红色, 5 n 5n 页绿色, 8 n 8n 张蓝色,问最少需要多少本笔记本。 n , k 1 0 8 n,k\leq 10^8

【解题思路】
颜色不相关,分开计算即可。

【参考代码】

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
ll n,k,ans;


int read()
{
	int ret=0,f=1;char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return f?ret:-ret;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("A.in","r",stdin);
	freopen("A.out","w",stdout);
#endif
	n=read();k=read();
	ans=(ll)(n*2-1)/k+1+(ll)(n*5-1)/k+1+(ll)(n*8-1)/k+1;
	printf("%lld\n",ans);

	return 0;
}

B.Margarite and the best present

【题目大意】
数列 a i = i ( 1 ) i a_i = i \cdot (-1)^i q q组询问 i = l r a i \sum_{i=l}^r a_i q 1 0 3 , l r 1 0 9 q\leq 10^3,l\leq r\leq 10^9

【解题思路】
差分求前缀和,每两个元素看成一组计算即可。

【参考代码】

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
ll q,l,r,ans;

int read()
{
	int ret=0,f=1;char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return f?ret:-ret;
}

ll calc(ll x)
{
	if(x%2==0) return x/2;
	return x/2-x;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("B.in","r",stdin);
	freopen("B.out","w",stdout);
#endif
	q=read();
	while(q--)
	{
		ll l=read(),r=read();
		ans=calc(r)-calc(l-1);
		printf("%lld\n",ans);
	}

	return 0;
}

C.Masha and two friends

【题目大意】
一个 n × m n\times m 的国际象棋棋盘,左下角为白格,接下来将棋盘的一个矩形所有格染白,再将另一个矩形所有格染黑,问白格和黑格各有多少个,多组数据。 T 1 0 3 , n , m 1 0 9 T\leq 10^3,n,m\leq 10^9

【解题思路】
计算出每个矩形对应原始棋盘的多少个黑格及白格,容斥计算即可。

【参考代码】

#include<bits/stdc++.h>
#define mkp make_pair
#define fi first
#define se second
using namespace std;

typedef long long ll;
typedef pair<ll,ll> pii;
ll n,m;
pii ans;

ll read()
{
	ll ret=0,f=1;char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return f?ret:-ret;
}

struct point{ll x,y;};
struct node
{
	point a,b;
	node(){}
	node(ll x,ll y,ll xx,ll yy){a.x=x;a.y=y;b.x=xx;b.y=yy;}
	void init(){a.x=read();a.y=read();b.x=read();b.y=read();}
}p1,p2,emp;

pii operator -(const pii&x,const pii &y){return mkp(x.fi-y.fi,x.se-y.se);} 
pii operator +(const pii&x,const pii &y){return mkp(x.fi+y.fi,x.se+y.se);} 
pii calc(ll x,ll y)
{
	if(!x || !y) return mkp(0,0);
	if((ll)x*y%2==0) return mkp((ll)x*y/2,(ll)x*y/2);
	return mkp((ll)x*y/2+1,(ll)x*y/2);
}
pii get(const point &a,const point &b){return calc(b.x,b.y)-calc(b.x,a.y-1)-calc(a.x-1,b.y)+calc(a.x-1,a.y-1);}
ll getsize(const point &a,const point &b){return (ll)(b.x-a.x+1)*(b.y-a.y+1);}

node getmerge(node a,node b)
{
	if(a.a.x>b.a.x) swap(a,b);
	if(b.a.x>a.b.x) return emp;
	if(b.b.y<a.a.y) return emp;
	if(b.a.y>a.b.y) return emp;
	return node(max(a.a.x,b.a.x),max(a.a.y,b.a.y),min(a.b.x,b.b.x),min(a.b.y,b.b.y));
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("C.in","r",stdin);
	freopen("C.out","w",stdout);
#endif
	int T=read();emp=node(-1,-1,-1,-1);
	while(T--)
	{
		n=read();m=read();
		p1.init();p2.init();
		pii t1=calc(n,m),t2=get(p1.a,p1.b),t3=get(p2.a,p2.b);
		node tmp=getmerge(p1,p2);
		//tmp.write();
		if(tmp.a.x==-1)
		{
			ans.fi=t1.fi-t2.fi-t3.fi+getsize(p1.a,p1.b);
			ans.se=t1.se-t2.se-t3.se+getsize(p2.a,p2.b);
		}
		else
		{
			pii t4=get(tmp.a,tmp.b);
			ans.fi=t1.fi-t2.fi-t3.fi+t4.fi+getsize(p1.a,p1.b)-getsize(tmp.a,tmp.b);
			ans.se=t1.se-t2.se-t3.se+t4.se+getsize(p2.a,p2.b);
		}
		printf("%lld %lld\n",ans.fi,ans.se);
	}

	return 0;
}

D.Olya and magical square

【题目大意】
初始有一个边长为 2 n 2^n 的正方形,进行 k k 次操作,每次选择一个正方形,将它分成四个等大的正方形。问是否能在 k k 次操纵后从左下角的正方形走到右上角的正方形,且经过的正方形边长一样,多组数据。 T 1 0 3 , n 1 0 9 , k 1 0 18 T\leq 10^3,n\leq 10^9,k\leq 10^{18}

【解题思路】
假设最后路径一定是贴着左、上边界,我们只需要枚举答案,然后判断是否可行即可。判断可行计算出答案需要至少切多少刀以及最多能切多少刀,可以发现答案一定很小(不到 64 64 吧)。

【参考代码】

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll INF=0x3f3f3f3f3f3f3f3f;
int T,n,ans;
ll k,flag,sum,cst,val;

int main()
{
#ifndef ONLINE_JUDGE
	freopen("D.in","r",stdin);
	freopen("D.out","w",stdout);
#endif
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%lld",&n,&k);flag=0;sum=val=0;cst=1;
		if(!k) {printf("YES %d\n",n);continue;}
		for(int i=n-1;~i;--i,cst=cst*2+3)
		{
			sum+=((ll)1<<n-i)-1;
			if(sum>k) break;
			if(i>=32) val=INF;
			else if(val!=INF) val+=(ll)cst*(((ll)1<<(i<<1))-1)/(ll)3;
			if(sum+val>=k){ans=i;flag=1;break;}
		}
		if(!flag) puts("NO");
		else printf("YES %d\n",ans);
	}

	return 0;
}

E. Sonya and Matrix Beauty

【题目大意】
给定一个只有小写字母的 n × m n\times m 矩阵,问有多少个子矩阵满足:存在一种方式,使得将每一行的字符分别重排后,这个子矩阵的每一行每一列均构成一个回文串。 n , m 250 n,m\leq 250

【解题思路】
考虑枚举矩阵的左右边界,现在要求有多少组上下边界符合题意。观察到当且仅当两行字母集合及每个字母数量完全相同时,这两行可以对应作为列的回文串构成,我们对每一行进行哈希。接下来每一行我们得到了一个哈希值,对这些哈希值做 manacher \text{manacher} (插入分隔哈希值,如 1 -1 ),我们可以得到最大扩展,显然以每个哈希值为中心的最大扩展除以 2 2 可以累加到答案。

需要注意的是某些行不可能成为回文串(包括分割行),做 manacher \text{manacher} 前标记即可。哈希值可以在循环枚举列的过程中处理出来。

复杂度 O ( n 3 ) O(n^3)

【参考代码】

#include<bits/stdc++.h>
using namespace std;

typedef unsigned long long ull;
typedef long long ll;
const int N=555,bas=233;
int n,m,s[N],p[N],f[N],num[N][N];
ll ans;
ull fc[N],hs[N],a[N<<1];
char mp[N][N];

void calc()
{
	for(int i=1;i<=2*n+2;++i) f[i]=0;
	for(int i=n;i;--i)
	{
		if(s[i]<2) a[i<<1]=hs[i]; else f[i<<1]=1;
		a[(i<<1)-1]=-1;
	}
	int len=n<<1|1,mx=0,id=0;
	f[0]=f[len+1]=1;a[len]=-1;
	for(int i=1;i<=len;++i)
	{
		if(mx>i) p[i]=min(mx-i,p[(id<<1)-i]); else p[i]=1;
		while(!f[i-p[i]] && !f[i+p[i]] && a[i-p[i]]==a[i+p[i]]) ++p[i];
		if(i+p[i]>=mx) mx=i+p[i],id=i;
	}
	for(int i=1;i<=len;++i) if(!f[i]) ans+=p[i]/2;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("CF1080E.in","r",stdin);
	freopen("CF1080E.out","w",stdout);
#endif
	fc[0]=1;for(int i=1;i<30;++i) fc[i]=fc[i-1]*bas;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) scanf("%s",mp[i]+1);
	for(int i=1;i<=m;++i)
	{
		for(int j=1;j<=n;++j) 
		{
			hs[j]=0;s[j]=0;
			for(int k=0;k<=26;++k) num[j][k]=0;
		}
		for(int j=i;j<=m;++j)
		{
			for(int k=1;k<=n;++k)
			{
				int x=mp[k][j]-'a'+1;
				num[k][x]++;hs[k]+=fc[x];
				if(num[k][x]&1) ++s[k]; else --s[k];
			}
			calc();
		}
	}
	printf("%lld\n",ans);

	return 0;
}

F. Katya and Segments Sets

【题目大意】
一共有 n n 个集合, m m 个询问和总计 k k 个二元组 ( x , y ) (x,y) (满足 x y x\leq y ),每个二元组属于一个集合 p p (当然可能有相同的二元组)。每个询问有四个整数 l , r , a , b l,r,a,b ,问对于 [ l , r ] [l,r] 的每个集合,是否都存在至少一个二元组满足 a x , y b a\leq x,y \leq b n , m 1 0 5 , k 3 × 1 0 5 n,m\leq 10^5,k\leq 3\times 10^5 ,所有数字 1 0 9 \leq 10^9 。询问强制在线。

【解题思路】
一道看起来是个三维偏序的题目。
我们先对这些二元组按 x x 从大到小排序,那么对于每个询问,我们可以转化为二元组的一个前缀集合中,是否对于每个集合 p p 都存在 y r y\leq r
考虑建出一棵以集合编号为下标的线段树,那么我们只需要在线段树上维护每个集合现在拥有的最小的 r r 即可。
于是我们用可持久化线段树实现这个过程,每次新加入一个二元组我们更新对应集合的最小的 r r ,最后询问如上即可。

复杂度 O ( ( k + m ) log k ) O((k+m)\log k) ,甚至不需要离散化。

【参考代码】

#include<bits/stdc++.h>
using namespace std;

const int N=3e5+10,M=N*40;
const int INF=0x3f3f3f3f;
int n,m,k,rt[N];

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}

struct data
{
	int l,r,p;
	data(int l=0,int r=0,int p=0):l(l),r(r),p(p){}
	void input(){l=read();r=read();p=read();}
	bool operator < (const data &x)const{return l>x.l || (l==x.l && r>x.r);}
}a[N],t;

struct Segment
{
	int sz,mi[M],ls[M],rs[M];
	void init(){memset(mi,0x3f,sizeof(mi));}
	void copy(int x,int y){mi[x]=mi[y];ls[x]=ls[y];rs[x]=rs[y];}
	void pushup(int x){mi[x]=max(mi[ls[x]],mi[rs[x]]);}
	void update(int &x,int y,int l,int r,int p,int v)
	{
		x=++sz;copy(x,y);
		if(l==r){mi[x]=min(mi[x],v);return;}
		int mid=(l+r)>>1;
		if(p<=mid) update(ls[x],ls[y],l,mid,p,v);
		else update(rs[x],rs[y],mid+1,r,p,v);
		pushup(x);
	}
	int query(int x,int l,int r,int L,int R)
	{
		if(!x) return INF;
		if(L<=l && r<=R) return mi[x];
		int mid=(l+r)>>1,ret=0;
		if(L<=mid) ret=max(ret,query(ls[x],l,mid,L,R));
		if(R>mid) ret=max(ret,query(rs[x],mid+1,r,L,R));
		return ret?ret:INF;
	}
}tr;

int main()
{
#ifndef ONLINE_JUDGE
	freopen("CF1080F.in","r",stdin);
	freopen("CF1080F.out","w",stdout);
#endif
	n=read();m=read();k=read();
	for(int i=1;i<=k;++i) a[i].input();
	sort(a+1,a+k+1);tr.init();
	for(int i=1;i<=k;++i) tr.update(rt[i],rt[i-1],1,n,a[i].p,a[i].r);
	for(int i=1;i<=m;++i)
	{
		int l=read(),r=read(),x=read(),y=read();
		int p=lower_bound(a+1,a+k+1,data(x,0,0))-a-1;
		int mx=tr.query(rt[p],1,n,l,r);
		puts(mx<=y?"yes":"no");
		fflush(stdout);
	}
	return 0;
}

【总结】
比赛时巨蒻,需要多打一些qwq。

猜你喜欢

转载自blog.csdn.net/Dream_Lolita/article/details/84564756