【Codeforces】 Round #524 (Div. 2) A-F

版权声明:欢迎转载(请附带原链接)ヾ(๑╹◡╹)ノ" https://blog.csdn.net/corsica6/article/details/84501936

传送门:CF524Div2


A. Petya and Origami

上取整

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

int n,m,k;ll a,b,c;

int main(){
	int i,j;
	scanf("%d%d",&n,&k);
	a=n*2;b=n*5;c=n*8;
    int d=0;
    printf("%I64d\n",(a-1)/k+1+(b-1)/k+1+(c-1)/k+1);
    return 0;
}

B. Margarite and the best present

前缀和 s i = ( 1 ) i i 2 s_i=(-1)^i\lceil\dfrac i2\rceil ,答案 s r s l 1 s_r-s_{l-1}

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

int n,m,k,x,y;

int main(){
	int i,j;
	scanf("%d",&m);
	for(;m;--m){
		scanf("%d%d",&x,&y);
		x--;y=((y&1)?(-1):1)*((y-1)/2+1);
		if(!x){
			printf("%d\n",y);
		}else{
			x=((x&1)?(-1):1)*((x-1)/2+1);
			printf("%d\n",y-x);
		}
	}
	return 0;
}

C. Masha and two friends

这题乍一看细节很多的样子。。。
可以根据左下角为白/黑的情况讨论出一个矩形中白黑格子个数。
分别处理两种染色的情况,再求两个矩形的并之后染成黑色即可。

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

int n,m,q,k;
int xa,xb,ya,yb;
int xc,xd,yc,yd;
ll a,b;//a->wt b->blk
ll x,y;

inline void col(int xa,int ya,int xb,int yb)
{
	x=0,y=0;//x->wt y->blk
	xb=xb-xa+1;yb=yb-ya+1;
	if((xa+ya)&1){
		x=y=(ll)xb*(yb/2);
		if(yb&1){
			x+=xb/2;y+=xb/2;
			if(xb&1) y++;
		}
	}else{
		x=y=(ll)xb*(yb/2);
		if(yb&1){
			x+=xb/2;y+=xb/2;
			if(xb&1) x++;
		}
	}
}

int main(){
	int i,j;
	scanf("%d",&q);
	for(;q;--q){
       scanf("%d%d",&n,&m);
       scanf("%d%d%d%d",&xa,&ya,&xb,&yb);
       scanf("%d%d%d%d",&xc,&yc,&xd,&yd);
       a=b=(ll)n*(m/2);
       if(m&1){
       	  a+=n/2;b+=n/2;
       	  if(n&1) a++;
       }
       col(ya,xa,yb,xb);b-=y;a+=y;
       col(yc,xc,yd,xd);a-=x;b+=x;
       ya=max(ya,yc);yb=min(yb,yd);
       xa=max(xa,xc);xb=min(xb,xd);
       if((ya<=yb)&&(xa<=xb)){
       	col(ya,xa,yb,xb);
        a-=y;b+=y;
       }
       printf("%I64d %I64d\n",a,b);
	}
	return 0;
}

D. Olya and magical square

贪心保留沿左上边界的路径。
我们逐次一起切割这条路径上的格子,设剩下格子最多可以被切割的次数为 h v hv ,则当 h v hv\geq 剩余的切割次数时,就有解了。

t i t_i 表示边长为 2 i 2^i 的正方形最多可以被切割的次数,存在递推式 t i = 4 t i 1 + 1 t_i=4t_{i-1}+1
a d i ad_i 表示第 i i 次切割前路径上的格子数, a d i = 2 i 1 ad_i=2^i-1
r e s i res_i 表示第 i i 次切割后新增加的剩下格子, r e s i = ( 2 a d i 1 ) t n i res_i=(2ad_i-1)*t_{n-i}

显然 n &gt; 32 n&gt;32 时,答案会直接超过 1 0 1 8 10^18 次方,所以强制 n 32 n\leq32 来判断即可。

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

int n,q,k,mx,nx;
ll m,t[50],ad[50],bin[50],res[50];

int main(){
	int i,j,pr,lim;ll hv;
	mx=32;t[1]=bin[0]=1;
	for(i=1;i<=mx;++i) bin[i]=bin[i-1]<<1;
	for(i=2;i<=mx;++i) t[i]=max(t[i-1],4*t[i-1]+1LL);
	for(i=1;i<=mx;++i) ad[i]=bin[i]-1;
	scanf("%d",&q);
	for(;q;--q){
        scanf("%d%I64d",&n,&m);
        hv=0LL;pr=0;
		lim=min(n,32);
		for(i=1;i<=lim;++i) res[i]=(ad[i]*2-1)*t[lim-i];
        for(i=1;i<=lim;++i){
        	m-=ad[i];hv+=res[i];
			if(m<0) break;
			if(hv>=m){printf("YES %d\n",n-i);pr=1;break;}
		}
		if(pr) continue;
		puts("NO");
	}
	return 0;
}

E. Sonya and Matrix Beauty

行回文需满足:出现次数为奇数的字母个数 1 \leq1
列回文需满足:围绕回文中心对应的两行所有字母出现次数相同。

考虑先求出每个字母的前缀矩形的出现次数和。

发现矩形的左右边界固定时,只需要求出 1 n 1-n 每行作为回文中心时的回文串长度,就统计出了答案,这个过程用 m a n a c h e r manacher 算法可以优化到 O ( n ) O(n)

所以枚举左右边界做 m a n a c h e r manacher ,复杂度 O ( 26 n m 2 ) O(26nm^2) ,可以哈希优化成 O ( n m 2 ) O(nm^2)

#include<bits/stdc++.h>
using namespace std;
const int N=252;
int n,m,ans,mx;
int cn[N<<1],f[N<<1][26],s[N][N][26];
int pl[N<<1];
char a[N];

inline int calc()
{
	int i,k,ct=0,dr=0,re=0,ty,pr;
	for(i=1;i<=mx;++i){
		if(!cn[i]) {pl[i]=0;continue;}ty=0;
		if(dr>i) ty=min(dr-i,pl[(ct<<1)-i]);
		for(;(i+ty<mx) && (i>ty+1) && cn[i+ty+1] && cn[i-ty-1];++ty){
			for(pr=k=0;k<26;++k)
			    if(f[i-ty-1][k]!=f[i+ty+1][k]) {pr=1;break;}
		    if(pr) break;
		}
		pl[i]=ty;if(i+ty>dr) ct=i,dr=i+ty;
	}
	for(i=1;i<=mx;++i)
	  if(cn[i] && pl[i]) re+=(pl[i]-1)/2+1;
	return re;
}

int main(){
	int i,j,k,t,cnt;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;++i){
        scanf("%s",a+1);
        for(j=1;j<=m;++j){
        	for(k=0;k<26;++k)
        		s[i][j][k]=s[i-1][j][k]+s[i][j-1][k]-s[i-1][j-1][k];
        	s[i][j][a[j]-'a']++;
        }
	}
	mx=n+n+1;
	for(i=1;i<=mx;++i) cn[i]=1;
	for(i=1;i<=m;++i)
	 for(j=i;j<=m;++j){
	 	for(k=1;k<=n;++k){
	 		cnt=0;
	 		for(t=0;t<26;++t){
	 			f[k<<1][t]=s[k][j][t]-s[k-1][j][t]-s[k][i-1][t]+s[k-1][i-1][t];
	 			if(f[k<<1][t]&1) cnt++;
	 		}
	 		cn[k<<1]=(cnt<2);
	 	}
	 	ans+=calc();
	 }
	 printf("%d",ans);
	 return 0;
}

F. Katya and Segments Sets

将所有区间元素按 l l 升序排序。

发现 l l 固定时,只有每个集合中元素 r r 最小的区间才有用。于是考虑按区间倒序建立主席树,第 x x 颗线段树中下标 1 n 1-n 分别表示集合 i i 中所有 l x l\geq x 的元素中 r r 的最小值。

对于每次询问二分找出最小的 l x l\geq x 的元素,以它为根的线段树中区间 [ a , b ] [a,b] 的最大值 m x mx ,若 m x y mx\leq y ,则输出 y e s yes ,否则输出 n o no

#include<bits/stdc++.h>
#define mid ((l+r)>>1)
using namespace std;
const int N=3e5+10,M=2e7+10,inf=0x3f3f3f3f;
 
int n,m,K,mn[N],st[N];
int rt[N],ls[M],rs[M],vl[M],cnt;

struct P{
	int l,r,p;
	inline void ini(){scanf("%d%d%d",&l,&r,&p);}
	bool operator<(const P&ky)const{
	     return l<ky.l;
	}
}le[N];

void mk(int pre,int &k,int l,int r,int pos,int vv)
{
	if(k==pre){k=++cnt;ls[k]=ls[pre];rs[k]=rs[pre];}
	if(l==r) {vl[k]=vv;return;}
	if(pos<=mid) mk(ls[pre],ls[k],l,mid,pos,vv);
	else mk(rs[pre],rs[k],mid+1,r,pos,vv);
	vl[k]=max(vl[ls[k]],vl[rs[k]]);
}

int ask(int k,int l,int r,int L,int R)
{
	if(!k) return inf;
	if(L<=l && r<=R) return vl[k];
    if(R<=mid) return ask(ls[k],l,mid,L,R);
    if(L>mid) return ask(rs[k],mid+1,r,L,R);
    return max(ask(ls[k],l,mid,L,R),ask(rs[k],mid+1,r,L,R));
}

int main(){
	 int i,j,a,b,x,y,re;vl[0]=inf;
	 memset(mn,0x3f,sizeof(mn));
	 scanf("%d%d%d",&n,&m,&K);
	 for(i=1;i<=K;++i) le[i].ini();
	 sort(le+1,le+K+1);
	 for(i=1;i<=K;++i) st[i]=le[i].l;
	 for(i=K;i;--i){
	 	rt[i]=rt[i+1];
	 	if(le[i].r<mn[le[i].p]){
	 		mk(rt[i+1],rt[i],1,n,le[i].p,le[i].r);
	 		mn[le[i].p]=le[i].r;
	 	}
	 }st[K+1]=inf;
	 for(;m;--m){
	 	scanf("%d%d%d%d",&a,&b,&x,&y);
	 	re=lower_bound(st+1,st+K+2,x)-st;
	 	if(re==K+1) re=inf;
	 	else re=ask(rt[re],1,n,a,b);
	 	puts(re<=y?"yes":"no");
		fflush(stdout);
	 }
	 return 0;
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/84501936