移动端没做适配,所以看起来十分诡异是正常的

【JOI】JOISC2020R1_T1building_构造/ntt

题面

大意就是:给两个\(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;
}
posted @ 2020-03-21 00:15  czyarl  阅读(486)  评论(5编辑  收藏  举报