题面
大意就是:给两个\(2n\)数组\(A,B\),要生成一个单调不降序列\(C\),使得\(C_i=A_i\)或\(C_i=B_i\)。并且有恰好\(n\)个位置选择了\(C_i=A_i\)。任意一种方案。
题解
现场得分:100/100
- 记\(A\)数组为第0个数组,\(B\)数组为第1个数组。
- 记dp:\(f_{i,0/1,0/1}\),表示从\(1\)到\(i\),第\(i\)个当中用的是第0/1个数组,尽可能地多用0/1,最多能用多少个。
- 这个很好转移
- 然后有个结论:如果\(n\leq min(f_{2n,0,0},f_{2n,0,1})\)或者\(n\leq min(f_{2n,1,0},f_{2n,1,1})\)就一定有解。感性地理解,就是你尽可能选0,可以超过一半;尽可能选1,也可以超过一半。
- 为什么?发现其中关键是能否在这个框出的区间里每个值都取到。(最多\(x\)个,最少\(y\)个,那么\([y,x]\)每个值都能取到)。
- 考虑相邻两个的关系。
- 先简化一下,我们记\(min_i=min(a_i,b_i),max_i=max(a_i,b_i)\)把第\(i\)个位置上的看成一个\(p_i=[min_i,max_i]\)的区间,发现这是不影响的。
- 如果\(p_i\)与\(p_{i+1}\)相离,显然不影响。
- 如果\(p_i\)与\(p_{i+1}\)有包含关系,显然就没有自由选择余地了,不影响。
- 如果\(p_i\)与\(p_{i+1}\)交叉,但是\(min_{i+1}<min_{i}\),显然就没有自由选择余地了,不影响。
- 剩下一种情况:\(min_i\leq min_{i+1}\leq max_i\leq max_{i+1}\)。你会发现这是两个递增序列,你随时可以从底下一个跳到上面一个,因此也是满足的。(如果没有看懂,底下有更详细的说明)
- 我们就证完了
- 然后考虑怎么构造
- 我们倒着跑一遍,每时每刻都保持尽量选0和尽量选1的最大个数始终大于等于\(n\)。
UPDATE:关于第四种情况的证明
他就是这种情况。如果所有的都是\(A_i>B_i\),那当然很简单了。但是他会鱼龙混杂,斑驳不堪,反复横跳,让人眼花缭乱,不知所措,被水淹没。
我们就把这段拎出来,假装长度是\(m\)好了。我们发现我们有\(m+1\)种选择,是先选一段min,之后选一段max。
-
我们记\(f(i)\)表示你先取\(i-1\)个min,然后跃迁(?)到max上你能选择多少个A数组元素。
-
显然,我们有\(f(i+1)=f(i)\pm 1\)。你就可以看成这个函数在整数域上是连续的。
-
既然连续,那么\([\min f(i),\max f(i)]\)中间所有值显然都能取到。
证完了
UPDATE2:2020-06-18 拓展
这个做法还可以拓展:我们可以用这个方式来统计有多少种方案。
- 就是每一段可以任意跳跃的,搞出一个这一段选几个\(A\)的方案数。
- 最后用ntt合并每段的答案。
代码
这个是原题
#include<bits/stdc++.h>
#define LL long long
#define MAXN 500100
using namespace std;
template<typename T>void Read(T &cn)
{
char c;int sig = 1;
while(!isdigit(c = getchar()))if(c == '-')sig = -1; cn = c-48;
while(isdigit(c = getchar()))cn = cn*10+c-48; cn*=sig;
}
template<typename T>void Write(T cn)
{
if(cn < 0) {putchar('-'); cn = 0-cn; }
int wei = 0; T cm = 0; int cx = cn%10; cn/=10;
while(cn)cm = cm*10+cn%10,cn/=10,wei++;
while(wei--)putchar(cm%10+48),cm/=10;
putchar(cx+48);
}
int n;
int a[MAXN*2+1], b[MAXN*2+1];
int ans[MAXN*2+1];
int f[MAXN*2+1][2][2];
void getit(int a[], int n) {for(int i = 1;i<=n;i++) Read(a[i]); }
void geng(int cn, int cm, int cx, int cy) {if(cy > f[cm][cx][cn]) f[cm][cx][cn] = cy; }
int nong()
{
for(int i = 0;i<=1;i++) for(int j = 0;j<=1;j++) f[1][i][j] = (i==j);
for(int i = 2;i<=n*2;i++)
{
for(int j = 0;j<=1;j++) for(int k = 0;k<=1;k++) f[i][j][k] = -n*2;
if(a[i] >= a[i-1]) geng(0,i,0,f[i-1][0][0]+1), geng(1,i,0,f[i-1][0][1]);
if(a[i] >= b[i-1]) geng(0,i,0,f[i-1][1][0]+1), geng(1,i,0,f[i-1][1][1]);
if(b[i] >= a[i-1]) geng(0,i,1,f[i-1][0][0]), geng(1,i,1,f[i-1][0][1]+1);
if(b[i] >= b[i-1]) geng(0,i,1,f[i-1][1][0]), geng(1,i,1,f[i-1][1][1]+1);
}
int lei1 = 0, lei2 = 0, lst = max(a[2*n],b[2*n])+1;
for(int i = n*2;i>=1;i--)
{
if(lei1 + f[i][0][0] >= n && lei2 + f[i][0][1] >= n && a[i] <= lst) {lei1++; ans[i] = 0; lst = a[i]; continue; }
if(lei1 + f[i][1][0] >= n && lei2 + f[i][1][1] >= n && b[i] <= lst) {lei2++; ans[i] = 1; lst = b[i]; continue; }
return 0;
}
return 1;
}
int main()
{
Read(n);
getit(a,n*2); getit(b,n*2);
if(!nong()) {puts("-1"); return 0; }
for(int i = 1;i<=n*2;i++) putchar('A'+ans[i]); puts("");
return 0;
}
这个是计数
#include<bits/stdc++.h>
#define LL long long
#define YG 3
#define MOD 998244353
#define MAXN 200000
using namespace std;
template<typename T>void Read(T &cn)
{
char c; int sig = 1;
while(!isdigit(c = getchar())) if(c == '-') sig = -1; cn = c-48;
while(isdigit(c = getchar())) cn = cn*10+c-48; cn*=sig;
}
template<typename T>void Write(T cn)
{
if(cn < 0) {putchar('-'); cn = 0-cn; }
int wei = 0; T cm = 0; int cx = cn%10; cn/=10;
while(cn) wei++, cm = cm*10+cn%10, cn/=10;
while(wei--) putchar(cm%10+48), cm /= 10;
putchar(cx+48);
}
template<typename T>void Max(T &cn, T cm) {cn = cn < cm ? cm : cn; }
template<typename T>void Min(T &cn, T cm) {cn = cn < cm ? cn : cm; }
const int MAXNTT = MAXN*4+1;
int omg[MAXNTT], inv[MAXNTT], Mn;
int erwei(int cn) {int guo = 0; while(cn) guo++, cn>>=1; return guo; }
LL ksm(LL cn, LL cm) {LL ans = 1; while(cm) ans = ans*(1+(cn-1)*(cm&1))%MOD, cn = cn*cn%MOD, cm>>=1; return ans; }
void yuchu_omg(int cn)
{
Mn = 1<<erwei(cn*4);
omg[0] = inv[0] = 1;
omg[1] = ksm(YG, MOD/Mn); inv[1] = ksm(omg[1], MOD-2);
for(int i = 2;i<Mn;i++) omg[i] = 1ll*omg[i-1]*omg[1]%MOD, inv[i] = 1ll*inv[i-1]*inv[1]%MOD;
}
struct Poly{
int a[MAXNTT], n, fan[MAXNTT];
void qing(int cn) {for(int i = n;i<cn;i++) a[i] = 0; }
void yuchu_fan(int cn) {int lin = erwei(cn)-2; fan[0] = 0; for(int i = 1;i<cn;i++) fan[i] = (fan[i>>1]>>1)|((i&1)<<lin); }
void copy(int ca[], int cl, int cr) {n = 0; for(int i = cl;i<=cr;i++) a[n++] = ca[i]; }
void do_ntt(int omg[], int cn)
{
for(int i = 0;i<cn;i++) if(fan[i] > i) swap(a[fan[i]], a[i]);
for(int i = 2, m = 1;i<=cn;i = (m = i)<<1)
for(int j = 0;j<cn;j+=i)
for(int k = 0;k<m;k++)
{
int lin1 = a[j+k], lin2 = 1ll*a[j+k+m]*omg[Mn/i*k]%MOD;
a[j+k] = lin1+lin2>=MOD ? lin1+lin2-MOD : lin1+lin2;
a[j+k+m] = lin1-lin2>=0 ? lin1-lin2 : lin1-lin2+MOD;
}
}
void ntt(int cn) {yuchu_fan(cn); qing(cn); do_ntt(omg, cn); }
void intt(int cn) {yuchu_fan(cn); do_ntt(inv, cn); int lin = ksm(cn, MOD-2); for(int i = 0;i<cn;i++) a[i] =1ll*lin*a[i]%MOD; }
void outit() {for(int i = 0;i<n;i++) printf("%d ",a[i]); puts(""); }
}A, B, C;
void Poly_cheng(Poly &A, Poly &B, Poly &C)
{
int lin = A.n+B.n, lin2 = 1<<erwei(lin);
// A.outit(); B.outit();
A.ntt(lin2); B.ntt(lin2);
for(int i = 0;i<lin2;i++) C.a[i] = 1ll*A.a[i]*B.a[i]%MOD;
C.intt(lin2);
C.n = lin;
// C.outit();
}
int n;
int a[MAXN+1], b[MAXN+1];
int zong;
int he1[MAXN+1], he2[MAXN+1];
int ge[MAXN+1];
int zhi[MAXN+1], zlen;
int zuo[MAXN+1], you[MAXN+1], dlen;
void jia_zhi(int cn)
{
++dlen; zuo[dlen] = zlen+1;
for(int i = 0;i<=cn;i++) zhi[++zlen] = ge[i];
you[dlen] = zlen;
}
void tongji(int cl, int cr)
{
// printf("in tongji : cl = %d cr = %d\n",cl,cr);
if(cl > cr) return;
he1[cl-1] = he2[cr+1] = 0;
for(int i = cl;i<=cr;i++) he1[i] = he1[i-1] + (a[i] <= b[i]);
for(int i = cr;i>=cl;i--) he2[i] = he2[i+1] + (a[i] > b[i]);
int xiao = n*2, da = 0;
for(int i = cl;i<=cr+1;i++) Min(xiao, he1[i-1]+he2[i]), Max(da, he1[i-1]+he2[i]);
for(int i = xiao;i<=da;i++) ge[i] = 0;
for(int i = cl;i<=cr+1;i++) ge[he1[i-1]+he2[i]]++;
zong = zong + xiao;
// printf("da = %d xiao = %d\n",da,xiao);
for(int i = xiao;i<=da;i++) ge[i-xiao] = ge[i];
jia_zhi(da-xiao);
}
void cal_low(int cl, int cr) {for(int i = cl;i<=cr;i++) if(a[i] < b[i]) zong++; }
void cal_up(int cl, int cr) {for(int i = cl;i<=cr;i++) if(a[i] > b[i]) zong++; }
int pan(int cl1, int cr1, int cl2, int cr2)
{
if(cl1 > cr1) swap(cl1, cr1);
if(cl2 > cr2) swap(cl2, cr2);
// printf("%d %d %d %d\n",cl1,cr1,cl2,cr2);
if(cr2 < cl1) return 0;
if(cl2 < cl1 && cl1 <= cr2 && cr2 < cr1) return 1;
if(cl2 < cl1 && cr1 <= cr2) return 2;
if(cl1 <= cl2 && cr2 < cr1) return 3;
if(cl1 <= cl2 && cl2 < cr1 && cr1 <= cr2) return 4;
if(cr1 <= cl2) return 5;
}
int pan2(int cn, int cl, int cr)
{
if(cl > cr) swap(cl, cr);
if(cr < cn) return 1;
if(cl < cn && cn <= cr) return 2;
if(cn <= cl) return 3;
}
void get_ans(int cl, int cr)
{
if(cl >= cr) return;
int wei = cl, zhi2 = max(you[cl]-zuo[cl]+1, you[cr]-you[cl]);
for(int i = cl+1;i<=cr;i++)
{
int lin = max(you[i]-zuo[cl]+1, you[cr]-you[i]);
if(lin <= zhi2) zhi2 = lin, wei = i;
}
get_ans(cl, wei); get_ans(wei+1,cr);
A.copy(zhi, zuo[cl], you[wei]); B.copy(zhi, zuo[wei+1], you[cr]);
Poly_cheng(A, B, C);
for(int i = zuo[cl];i<=you[cr];i++) zhi[i] = C.a[i-zuo[cl]];
}
signed main()
{
Read(n); n*=2; yuchu_omg(n); Read(zong);
for(int i = 1;i<=n;i++) Read(a[i]);
for(int i = 1;i<=n;i++) Read(b[i]);
zong = 0; dlen = zlen = 0;
int tai = 0, zhi1 = min(a[1],b[1]), zhi2 = max(b[1],a[1]), tou = 1, j = 1; you[0] = 0;
if(zhi1 > zhi2) swap(zhi1, zhi2);
while(j < n)
{
if(tai == 0) {
int lin = pan(zhi1, zhi2, a[j+1], b[j+1]);
if(lin == 0) {zong = -1; break; }
if(lin == 1) {cal_low(tou,j); cal_up(j+1,j+1); tou = j+2; tai = 1; zhi1 = max(a[j+1], b[j+1]); }
if(lin == 2) {tongji(tou, j); cal_up(j+1,j+1); tou = j+2; tai = 1; zhi1 = max(a[j+1], b[j+1]); }
if(lin == 3) {cal_low(tou,j); tou = j+1; tai = 0; zhi1 = min(a[j+1],b[j+1]); zhi2 = max(a[j+1],b[j+1]); }
if(lin == 4) {tai = 0; zhi1 = min(a[j+1], b[j+1]); zhi2 = max(a[j+1], b[j+1]); }
if(lin == 5) {tongji(tou, j); tou = j+1; tai = 0; zhi1 = min(a[j+1],b[j+1]); zhi2 = max(a[j+1],b[j+1]); }
}
else {
int lin = pan2(zhi1, a[j+1], b[j+1]);
if(lin == 1) {zong = -1; break; }
if(lin == 2) {cal_up(j+1,j+1); tou = j+2; tai = 1; zhi1 = max(a[j+1], b[j+1]); }
if(lin == 3) {tou = j+1; tai = 0; zhi1 = min(a[j+1],b[j+1]); zhi2 = max(a[j+1],b[j+1]); }
}
j++;
}
if(zong == -1 || zong > n/2) {puts("0"); return 0; }
tongji(tou, n);
get_ans(1,dlen); if(!dlen) zhi[1] = 1;
Write(zhi[n/2-zong+1]); puts("");
return 0;
}