传送门: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 si=(−1)i⌈2i⌉,答案 s r − s l − 1 s_r-s_{l-1} sr−sl−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
hv,则当
h
v
≥
hv\geq
hv≥剩余的切割次数时,就有解了。
设
t
i
t_i
ti表示边长为
2
i
2^i
2i的正方形最多可以被切割的次数,存在递推式
t
i
=
4
t
i
−
1
+
1
t_i=4t_{i-1}+1
ti=4ti−1+1。
设
a
d
i
ad_i
adi表示第
i
i
i次切割前路径上的格子数,
a
d
i
=
2
i
−
1
ad_i=2^i-1
adi=2i−1。
设
r
e
s
i
res_i
resi表示第
i
i
i次切割后新增加的剩下格子,
r
e
s
i
=
(
2
a
d
i
−
1
)
∗
t
n
−
i
res_i=(2ad_i-1)*t_{n-i}
resi=(2adi−1)∗tn−i。
显然 n > 32 n>32 n>32时,答案会直接超过 1 0 1 8 10^18 1018次方,所以强制 n ≤ 32 n\leq32 n≤32来判断即可。
#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
列回文需满足:围绕回文中心对应的两行所有字母出现次数相同。
考虑先求出每个字母的前缀矩形的出现次数和。
发现矩形的左右边界固定时,只需要求出 1 − n 1-n 1−n每行作为回文中心时的回文串长度,就统计出了答案,这个过程用 m a n a c h e r manacher manacher算法可以优化到 O ( n ) O(n) O(n)。
所以枚举左右边界做 m a n a c h e r manacher manacher,复杂度 O ( 26 n m 2 ) O(26nm^2) O(26nm2),可以哈希优化成 O ( n m 2 ) O(nm^2) O(nm2)
#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 l l固定时,只有每个集合中元素 r r r最小的区间才有用。于是考虑按区间倒序建立主席树,第 x x x颗线段树中下标 1 − n 1-n 1−n分别表示集合 i i i中所有 l ≥ x l\geq x l≥x的元素中 r r r的最小值。
对于每次询问二分找出最小的 l ≥ x l\geq x l≥x的元素,以它为根的线段树中区间 [ a , b ] [a,b] [a,b]的最大值 m x mx mx,若 m x ≤ y mx\leq y mx≤y,则输出 y e s yes yes,否则输出 n o no 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;
}