⌈ 2 n k ⌉ + ⌈ 5 n k ⌉ + ⌈ 8 n k ⌉ \lceil\frac{2n}k\rceil+\lceil\frac{5n}k\rceil+\lceil\frac{8n}k\rceil ⌈k2n⌉+⌈k5n⌉+⌈k8n⌉
代码
#include#include#include#include#includeint n, k;intmain(){std::cin >> n >> k;std::cout <<(2* n + k -1)/ k +(5* n + k -1)/ k+(8* n + k -1)/ k << std::endl;return0;}
B
题意
给定一个数列 a a a ,满足 a i = i × ( − 1 ) i a_i=i\times(-1)^i ai=i×(−1)i
如果 r − l + 1 r-l+1 r−l+1 是奇数,就直接转化成区间 [ l , r − 1 ] [l,r-1] [l,r−1] 的和再加上 a r a_r ar ,显然区间 [ l , r − 1 ] [l,r-1] [l,r−1] 的长度是偶数
如果区间长度 r − l + 1 r-l+1 r−l+1 是偶数,那么
(1) l l l 是奇数时区间和为 r − l + 1 2 \frac{r-l+1}2 2r−l+1
(2) l l l 是偶数时区间和为 − r − l + 1 2 -\frac{r-l+1}2 −2r−l+1
代码
#include#include#include#include#includeinlineintread(){int res =0;bool bo =0;char c;while(((c =getchar())<'0'|| c >'9')&& c !='-');if(c =='-') bo =1;else res = c -48;while((c =getchar())>='0'&& c <='9')res =(res <<3)+(res <<1)+(c -48);return bo ?~res +1: res;}int q;intmain(){int l, r;q =read();while(q--){l =read(); r =read();int res = l &1? r - l +1>>1:-(r - l +1>>1);if(!(r - l &1)){if(r &1) res -= r;else res += r;}printf("%d\n", res);}return0;}
C
题意
给定一个 n × m n\times m n×m 的网格,对于第 i i i 行第 j j j 列,如果 i + j i+j i+j 为偶数则 ( i , j ) (i,j) (i,j) 为白色,否则为黑色
进行两次操作
第一次把一个子矩形全部染黑
第二次把一个子矩形全部染白
最后求棋盘上白、黑的格子数
多组数据(不超过 1 0 3 10^3 103 组), 1 ≤ n , m ≤ 1 0 9 1\le n,m\le 10^9 1≤n,m≤109
题解
操作之前一个子矩形的黑白格子数很容易计算
如果格子数为偶数,则黑白格子各占一半
否则与这个子矩形的左下角的颜色同色的格子数是该子矩形总格子数除以二后上取整
将两个子矩形分开处理即可
如果两个子矩形有交则需要处理相交部分,可以利用容斥的思想
代码
#include#include#include#include#includeinlineintread(){int res =0;bool bo =0;char c;while(((c =getchar())<'0'|| c >'9')&& c !='-');if(c =='-') bo =1;else res = c -48;while((c =getchar())>='0'&& c <='9')res =(res <<3)+(res <<1)+(c -48);return bo ?~res +1: res;}template<classT>inline T Min(const T &a,const T &b){return a < b ? a : b;}template<classT>inline T Max(const T &a,const T &b){return a > b ? a : b;}typedeflonglong ll;int n, m, X1, Y1, X2, Y2, X3, Y3, X4, Y4;
ll w, b;ll rects(int xl,int yl,int xr,int yr){if(!(1ll*(xr - xl +1)*(yr - yl +1)&1))return1ll*(xr - xl +1)*(yr - yl +1)>>1;if(xl + yl &1)return1ll*(xr - xl +1)*(yr - yl +1)>>1;return(1ll*(xr - xl +1)*(yr - yl +1)>>1)+1;}ll stcer(int xl,int yl,int xr,int yr){return1ll*(xr - xl +1)*(yr - yl +1)-rects(xl, yl, xr, yr);}voidwork(){n =read(); m =read();X1 =read(); Y1 =read(); X2 =read(); Y2 =read();X3 =read(); Y3 =read(); X4 =read(); Y4 =read();if(1ll* n * m &1) w =(1ll* n * m >>1)+1, b = w -1;else w = b =1ll* n * m >>1;w -=rects(X1, Y1, X2, Y2)+rects(X3, Y3, X4, Y4);b -=stcer(X1, Y1, X2, Y2)+stcer(X3, Y3, X4, Y4);w +=1ll*(X2 - X1 +1)*(Y2 - Y1 +1);b +=1ll*(X4 - X3 +1)*(Y4 - Y3 +1);if(Max(X1, X3)<=Min(X2, X4)&&Max(Y1, Y3)<=Min(Y2, Y4)){w +=rects(Max(X1, X3),Max(Y1, Y3),Min(X2, X4),Min(Y2, Y4));b +=stcer(Max(X1, X3),Max(Y1, Y3),Min(X2, X4),Min(Y2, Y4));w -=1ll*(Min(X2, X4)-Max(X1, X3)+1)*(Min(Y2, Y4)-Max(Y1, Y3)+1);}std::cout << w <<" "<< b << std::endl;}intmain(){int T =read();while(T--)work();return0;}
D
题意
给定一个边长为 2 n 2^n 2n 的正方形
需要进行恰好 k k k 次切割
每次把一个边长为 2 a 2^a 2a ( a > 0 a>0 a>0 )的正方形横切一下竖切一下,切割成 4 4 4 个边长为 2 a − 1 2^{a-1} 2a−1 的正方形
求是否存在一种切割方案使得下面的条件全部满足
(1)左下角和右上角的正方形大小相同
(2)存在一条路径,只经过与左下角和右上角大小相同的正方形,从左下角走到右上角。这条路径中相邻的两个正方形必须有公共边(只有公共顶点不行),设左下角和右上角的正方形大小都为 2 x 2^x 2x
首先,如果 n > 31 n>31 n>31 则存在一种方案 x = n − 1 x=n-1 x=n−1 ,直接输出即可
否则枚举 x x x ,下面以 x = n − 3 x=n-3 x=n−3 的情况为例
首先构造出这条路径
可以递推求出转到如上图这种状态的最少切割次数 q 1 q_1 q1
横切 2 n − x − 1 2^{n-x}-1 2n−x−1 刀,竖切 2 n − x − 1 2^{n-x}-1 2n−x−1 刀,均分成 2 2 ( n − x ) 2^{2(n-x)} 22(n−x) 个规模为 2 x 2^x 2x 的正方形
然后把除第一行、第一列之外的,所有规模为 2 x 2^x 2x 的 ( 2 n − x − 1 ) 2 (2^{n-x}-1)^2 (2n−x−1)2 个正方形全部切成 1 × 1 1\times1 1×1 的小正方形,这样就得到了答案为 x x x 时的最多切割次数 q 2 q_2 q2
如果 q 1 ≤ k ≤ q 2 q_1\le k\le q_2 q1≤k≤q2 则合法
复杂度 O ( t min ( n , 31 ) ) O(t\min(n,31)) O(tmin(n,31))
代码
#include#include#include#include#include#define For(i, a, b) for (i = a; i <= b; i++)#define Rof(i, a, b) for (i = a; i >= b; i--)constint N =40;typedeflonglong ll;int n;
ll k, f[N], g[N], p2[N];boolgreaters(ll a, ll b, ll c){if(c <0)return0;if(!b)return a * b <0;return a <(c + b -1)/ b;}voidwork(){int i, j;ll delta;std::cin >> n >> k;if(n >31)return(void)printf("YES %d\n", n -1);Rof (i, n -1,0){if(f[n - i]> k)continue;ll grids =1ll*(p2[n - i]-1)*(p2[n - i]-1);if(greaters(g[i], grids, k - g[n - i]))continue;return(void)printf("YES %d\n", i);}puts("NO");}intmain(){int i, j, T; std::cin >> T;f[1]= p2[0]= g[1]=1;For (i,1,35) p2[i]=2ll* p2[i -1];For (i,2,35) f[i]=3ll* f[i -1]+1,g[i]=4ll* g[i -1]+1;while(T--)work();return0;}
E
题意
给定一个 n × m n\times m n×m 字符矩阵
求有多少个非空子矩阵
满足能够对于子矩阵的每一行,将该行的字符重新排列
使得子矩阵的每行每列都是回文串
字符集为小写英文字母
1 ≤ n , m ≤ 250 1\le n,m\le250 1≤n,m≤250
算法: Hash + Manacher
小清新字符串题
先转化下一个子矩阵合法的条件
(1)子矩阵的每一行都能重组成回文串(出现次数为奇数的字符不超过 1 1 1 个)
(2)对于子矩阵的任意对称的两行 i i i 和 j j j 以及任意字符 c c c ,都满足子矩阵第 i i i 行字符 c c c 的出现次数与第 j j j 行字符 c c c 的出现次数相等
条件(1)很容易预处理出
而条件(2)可以对于每一行 i i i 的每一个区间 [ l , r ] [l,r] [l,r] ,预处理出区间内所有字符的出现次数组成的哈希值 h a s h [ i ] [ l ] [ r ] hash[i][l][r] hash[i][l][r]
那么条件(2)就转化成了如果子矩阵跨列 [ l , r ] [l,r] [l,r] ,第 i i i 行与第 j j j 行对称,则需要满足 h a s h [ i ] [ l ] [ r ] = h a s h [ j ] [ l ] [ r ] hash[i][l][r]=hash[j][l][r] hash[i][l][r]=hash[j][l][r]
有了这个条件,我们就可以开始统计了
枚举子矩阵跨列的范围 [ l , r ] [l,r] [l,r]
问题转化成有多少个区间 [ a , b ] [a,b] [a,b] 满足
(1)对于任意的 a ≤ i ≤ b a\le i\le b a≤i≤b 都满足第 i i i 行的 [ l , r ] [l,r] [l,r] 能重组成回文串
(2)对于任意的 a ≤ i , j ≤ b a\le i,j\le b a≤i,j≤b , i − a = b − j i-a=b-j i−a=b−j (其实就是 i , j i,j i,j 在区间 [ a , b ] [a,b] [a,b] 内对称),有 h a s h [ i ] [ l ] [ r ] = h a s h [ j ] [ l ] [ r ] hash[i][l][r]=hash[j][l][r] hash[i][l][r]=hash[j][l][r]
发现(2)表示 h a s h [ a . . . b ] [ l ] [ r ] hash[a...b][l][r] hash[a...b][l][r] 是一个回文数组
对 h a s h [ ] [ l ] [ r ] hash[][l][r] hash[][l][r] 数组跑 Manacher 统计,注意处理掉条件(1)
复杂度 O ( n m 2 ) O(nm^2) O(nm2)
代码
#include#include#include#include#include#define For(i, a, b) for (i = a; i <= b; i++)template<classT>inline T Min(const T &a,const T &b){return a < b ? a : b;}constint N =255, M =26, L = N <<1, ZZQ =998244353, YSY =1e9+7;int n, m, h1[N][N][N], h2[N][N][N], pw1[M], pw2[M], str1[L], str2[L],
ans, R[L], cnt[M];char s[N][N];bool pal[N][N][N];voidmanacher(int n){int i, mx =0, pos;For (i,1, n){R[i]= mx > i ?Min(mx - i, R[(pos <<1)- i]):1;while(str1[i - R[i]]== str1[i + R[i]]&& str2[i - R[i]]== str2[i + R[i]]) R[i]++;if(i + R[i]> mx) mx = i + R[i], pos = i;}}voidsolve(int l,int r){int i, tot =0;str1[0]= str2[0]=-19310918;For (i,1, n){str1[++tot]=-19370707;str2[tot]=-19370707;str1[++tot]= h1[i][l][r];str2[tot]= pal[i][l][r]? h2[i][l][r]:-i;}str1[++tot]=-19370707;str2[tot]=-19370707;str1[tot +1]= str2[tot +1]=-19450815;manacher(tot);For (i,1, tot)if(str2[i]<-1000|| str2[i]>=0)ans += i &1? R[i]-1>>1: R[i]>>1;}intmain(){int i, j, k;std::cin >> n >> m;pw1[0]= pw2[0]=1;For (i,1,25){pw1[i]=317ll* pw1[i -1]% ZZQ;pw2[i]=317ll* pw2[i -1]% YSY;}For (i,1, n)scanf("%s", s[i]+1);For (i,1, n) For (j,1, m){int o1 =0, o2 =0, cc =0;For (k,0,25) cnt[k]=0;For (k, j, m){int c = s[i][k]-'a';o1 =(o1 + pw1[c])% ZZQ;o2 =(o2 + pw2[c])% YSY;if((++cnt[c])&1) cc++;else cc--;pal[i][j][k]= cc <=1;h1[i][j][k]= o1; h2[i][j][k]= o2;}}For (i,1, m) For (j, i, m)solve(i, j);std::cout << ans << std::endl;return0;}
F
题意
给定 n n n 个集合
每个集合里有一些区间
有 m m m 个询问
每次给出 a , b , x , y a,b,x,y a,b,x,y
询问是否对于每个 i ∈ [ a , b ] i\in[a,b] i∈[a,b] ,都满足第 i i i 个集合内存在一个区间包含于 [ x , y ] [x,y] [x,y]
强制在线
1 ≤ n , m ≤ 1 0 5 1\le n,m\le 10^5 1≤n,m≤105 , 1 ≤ k ≤ 3 × 1 0 5 1\le k\le3\times10^5 1≤k≤3×105 ( k k k 为总区间个数)
区间端点在 [ 1 , 1 0 9 ] [1,10^9] [1,109] 内
算法:主席树
小清新数据结构题
先离散化区间端点
询问可以看成将所有右端点不超过 y y y 的区间全部加入,设这种情况下第 i i i 个集合内区间的左端点的最大值 为 l m a x i lmax_i lmaxi (第 i i i 个集合为空则 l m a x i = 0 lmax_i=0 lmaxi=0 )
那么就是询问 min i = a b l m a x i \min_{i=a}^blmax_i mini=ablmaxi 是否大于等于 x x x
如果大于等于 x x x 则输出 yes ,否则 no
建一些线段树,第 i i i 棵线段树储存所有右端点不超过 i i i 的区间全部加入时,这种情况下的 l m a x i lmax_i lmaxi 及其区间最小值
考虑到空间问题,使用主席树,将区间按右端点排序后,对于所有的 i i i ,将前 i i i 个区间建一个历史版本,第 i i i 个历史版本建在第 i − 1 i-1 i−1 个历史版本上
复杂度 O ( ( n + m + k ) log n ) O((n+m+k)\log n) O((n+m+k)logn)
代码
#include#include#include#include#include#define For(i, a, b) for (i = a; i <= b; i++)inlineintread(){int res =0;bool bo =0;char c;while(((c =getchar())<'0'|| c >'9')&& c !='-');if(c =='-') bo =1;else res = c -48;while((c =getchar())>='0'&& c <='9')res =(res <<3)+(res <<1)+(c -48);return bo ?~res +1: res;}template<classT>inline T Max(const T &a,const T &b){return a > b ? a : b;}template<classT>inline T Min(const T &a,const T &b){return a < b ? a : b;}constint N =3e5+5, M = N <<1, L =3e7+5;int n, m, k, tmp, arr[M], rt[N], pos[M], ToT;struct interval
{int l, r, p;} a[N];struct node
{int lc, rc, val;} T[L];inlineboolcomp(interval a, interval b){return a.r < b.r;}voidins(int y,int&x,int l,int r,int p,int val){T[x =++ToT]= T[y];if(l == r)return(void)(T[x].val =Max(T[x].val, val));int mid = l + r >>1;if(p <= mid)ins(T[y].lc, T[x].lc, l, mid, p, val);elseins(T[y].rc, T[x].rc, mid +1, r, p, val);T[x].val =Min(T[T[x].lc].val, T[T[x].rc].val);}intquery(int l,int r,int s,int e,int p){if(l == s && r == e)return T[p].val;int mid = l + r >>1;if(e <= mid)returnquery(l, mid, s, e, T[p].lc);elseif(s >= mid +1)returnquery(mid +1, r, s, e, T[p].rc);elsereturnMin(query(l, mid, s, mid, T[p].lc),query(mid +1, r, mid +1, e, T[p].rc));}intmain(){int i, _a, _b, x, y;n =read(); m =read(); k =read();For (i,1, k) a[i].l =read(), a[i].r =read(), a[i].p =read();For (i,1, k) arr[(i <<1)-1]= a[i].l, arr[i <<1]= a[i].r;std::sort(arr +1, arr +(k <<1)+1);tmp = std::unique(arr +1, arr +(k <<1)+1)- arr -1;For (i,1, k){a[i].l = std::lower_bound(arr +1, arr + tmp +1, a[i].l)- arr;a[i].r = std::lower_bound(arr +1, arr + tmp +1, a[i].r)- arr;}std::sort(a +1, a + k +1, comp);For (i,1, k){ins(rt[i -1], rt[i],1, n, a[i].p, a[i].l);pos[a[i].r]= i;}For (i,1, tmp)if(!pos[i]) pos[i]= pos[i -1];while(m--){_a =read(); _b =read(); x =read(); y =read();x = std::lower_bound(arr +1, arr + tmp +1, x)- arr;y = std::upper_bound(arr +1, arr + tmp +1, y)- arr -1;puts(x > y ||query(1, n, _a, _b, rt[pos[y]])< x ?"no":"yes");fflush(stdout);}return0;}