题目链接:https://cometoj.com/contest/39/problems
A.骚动时节的少女们
水题略。
B.奇偶性
1.当k是奇数时,所有数都是奇数,所以直接输出r-l+1.
2.当k是偶数时,显然是偶数的位置是p = x*(k+1) - 1,x = 1,2,3,4....,所以对于这种情况来说只要多做个除法就好了
#include
using namespace std;
typedef long long ll;
const int mx = 1e5 + 10;
int a[10];
int main(){
int t;scanf("%d",&t);
while(t--){
ll l,r,k;
scanf("%lld%lld%lld",&l,&r,&k);
if(k&1) printf("%lld\n",r-l+1);
else{
ll c = (r+1)/(k+1) - (l)/(k+1);
printf("%lld\n",r-l+1-c);
}
}
return 0;
}
C.方块切割
假设矩形有w块非障碍物要被平分,矩形被分成了x块,那么显然x肯定要整除于w。
并且我们可以从大到小枚举横线切割的数量,那么肯定第一个满足条件的肯定是字典序最小的,因为横线越多将矩形平分的越多,第一个出现的横线下标就越小。
还有一个简单的证明结论就是当我们确定了横线和竖线的切割数时,此时切割方案肯定是唯一的,不妨将竖线抛开,我们只看横线,如果每一行都至少有一个非障碍物,那么很明显横线切割方法唯一,如果存在某些行都是障碍物,那么为了字典序小,所以肯定将它放在后面。所以切割方案肯定唯一。对于竖线也是如此。
当我们分完切割线后,就要去查看每个块的非障碍物数量是否都相等就可以了,题目给的只能一维,但是我们可以用一维数组指针模拟二维。
#include
using namespace std;
typedef long long ll;
const int mx = 2e6 + 10;
int *a[mx],pr[mx],pc[mx];
int *f[mx],b[mx];
int n,m,ret[mx];
int q1[2010],q2[2010];
int is,sum[mx];
char s[mx];
bool vis[mx];
int get(int x1,int y1,int x2,int y2){
x1++,y1++;
return f[x2][y2] - f[x1-1][y2] - f[x2][y1-1] + f[x1-1][y1-1];
}
bool check(int x,int y,int siz){
int r = siz / (x+1),id;
int c = siz / (y+1),now,w = siz/(x+1)/(y+1);
int hd1 = 0,hd2 = 0;
id = now = is = 0;
for(int i=1;i<=n;i++){
now += pr[i];
if(id==x) continue;
if(now==r){
now = 0,id++;
ret[is++] = i,q1[++hd1] = i;
}
}
if(id!=x||now!=r) return 0;
id = now = 0;
for(int i=1;i<=m;i++){
now += pc[i];
if(id==y) continue;
if(now==c){
now = 0,id++;
ret[is++] = i+n-1,q2[++hd2] = i;
}
}
ret[is++] = 0;
if(id!=y||now!=c) return 0;
q1[++hd1] = n,q2[++hd2] = m;
for(int i=1;i<=hd1;i++){
for(int j=1;j<=hd2;j++){
if(get(q1[i-1],q2[j-1],q1[i],q2[j])!=w)
return 0;
}
}
return 1;
}
int main(){
int t,k;scanf("%d",&t);
while(t--){
int c = 0,*id = sum,*ip = b;
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<=n;i++){
f[i] = id,a[i] = ip;
id += m+1,ip += m+1;
for(int j=0;j<=m;j++)
f[i][j] = 0;
}
for(int i=0;i<=n;i++) pr[i] = 0;
for(int i=0;i<=m;i++) pc[i] = 0;
for(int i=1;i<=n;i++){
scanf("%s",s+1);
for(int j=1;j<=m;j++){
a[i][j] = !(s[j] - '0');
pr[i] += a[i][j];
pc[j] += a[i][j];
c += a[i][j];
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
f[i][j] = a[i][j] + f[i-1][j] + f[i][j-1] - f[i-1][j-1];
bool ok = 0;
for(int i=k;i>=0;i--){
if(c%((i+1)*(k-i+1))==0&&i
D.求和
没做,不过看起来应该不难
E.公共子序列
序列中值包含了123,想这种情况一般肯定是先枚举其中一个,然后剩下两个怎么办呢,总不能O(n^2)解决吧。
首先我们枚举最长公共子序列的前缀1的个数x,那么对于a和b序列还说肯定是前缀干好有x个1的位置,我们设为la,lb。
此时我们再去设定一个右边界ra和rb,使得[ra,n]和[rb,m]的区间中3的个数刚好为相同,然后在[la.ra]和[lb,rb]中的连续2的个数中取最小,然后答案就是x+z+min(s[la,ra],s[lb,rb]),但是我们要枚举z吗?我们再定义a和b的前缀2的个数的数组pa,pb。
假设s[la.ra]<=s[lb,rb],那么式子可以写为:
x - pa[la] + z + pa[ra](应该是pa[la-1]的,但是la位置肯定是1,所以pa[la]肯定等于pa[la-1])
由于la和x是关联的,z和ra也是关联的,而他们两者确实无关的,所以我们在枚举x的时候没有用一些数据结构去维护z+pa[ra]的最大值,由于pa[ra]-pa[la] <= pb[rb] - pb[lb]也就是pb[lb] - pa[la] <= pb[rb] - pa[ra],所以我们可以将pb[rb] - pa[ra]看做key, z + pa[ra]看做value,维护一个后缀最大值,然后用pb[lb] - pa[la]查询,因为x从大到小枚举,所以肯定保证key的位置在x值位置的后面。
当s[la.ra]>s[lb,rb]的情况类似。
#include
using namespace std;
const int mx = 1e6 + 10;
int n,m,pa[mx],pb[mx];
int a[mx],b[mx];
int L1[mx],L2[mx],R1[mx],R2[mx];
int aL,aR,bL,bR,tot;
int Ma[mx<<2],Mb[mx<<2];
void add(int x,int *p,int v){
x += tot / 2;
while(x){
p[x] = max(p[x],v);
x -= x & -x;
}
}
int getans(int x,int *p){
int ans = 0;
x += tot / 2;
while(x<=tot){
ans = max(ans,p[x]);
x += x & -x;
}
return ans;
}
int solve(){
tot = max(n,m)*2 + 10;
for(int i=1;i<=tot;i++) Ma[i] = Mb[i] = 0;
int ans = 0;
int now = 0,L = min(aL,bL),R = min(aR,bR);
for(int i=L;i>=0;i--){
int l1 = L1[i],l2 = L2[i];
while(now<=R&&R1[now]>l1&&R2[now]>l2)
{
int r1 = R1[now],r2 = R2[now];
add(pb[r2]-pa[r1],Ma,now+pa[r1]);
add(pa[r1]-pb[r2],Mb,now+pb[r2]);
now++;
}
ans = max(ans,getans(pb[l2]-pa[l1],Ma)+i-pa[l1]);
ans = max(ans,getans(pa[l1]-pb[l2],Mb)+i-pb[l2]);
}
return ans;
}
int main(){
int t;scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
aL = aR = bL = bR = 0;
for(int i=1;i<=n;i++) scanf("%d",a+i);
for(int i=1;i<=m;i++) scanf("%d",b+i);
for(int i=1;i<=n;i++){
pa[i] = pa[i-1] + (a[i]==2);
if(a[i]==1) L1[++aL] = i;
}
for(int i=1;i<=m;i++){
pb[i] = pb[i-1] + (b[i]==2);
if(b[i]==1) L2[++bL] = i;
}
R1[0] = n+1,R2[0] = m+1;
pa[n+1] = pa[n],pb[m+1] = pb[m];
for(int i=n;i>=1;i--) if(a[i]==3)
R1[++aR] = i;
for(int i=m;i>=1;i--) if(b[i]==3)
R2[++bR] = i;
printf("%d\n",solve());
}
return 0;
}