zoukankan      html  css  js  c++  java
  • 【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,最多能用多少个。
    • 这个很好转移
    • 然后有个结论:如果(nleq min(f_{2n,0,0},f_{2n,0,1}))或者(nleq 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_ileq min_{i+1}leq max_ileq 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
    ",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
    ",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
    ",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;
    }
    
  • 相关阅读:
    EJB>复合主键(Composite Primary Key)
    EJB>消息驱动beanTopic 消息的发送与接收(Pub/sub 消息传递模型)
    JSF>自订验证器
    EJB>自定义安全域
    EJB>Entity 的生命周期和状态、回调函数
    EJB>安全服务的具体开发
    单片机的中断系统
    JavaScript代码检查工具——JSLintMate
    如何开发一个 N9 上的程序
    NSIS安装制作基础教程
  • 原文地址:https://www.cnblogs.com/czyarl/p/12535922.html
Copyright © 2011-2022 走看看