【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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM