• [Contest on 2021.10.14] 我靠,真的好困!


    (sf CF1237D Balanced Playlist)

    解法

    考场上写了一个挺智障的 (mathcal O(nlog n)) 的写法…

    首先处理出 (nxt_i) 表示离第 (i) 首歌最近的会使其结束的下标,可以发现这是小于 (2n) 的。具体开一棵权值线段树,但注意相同权值不能去掉。遍历到 (i) 就将 (n+i-1) 加入线段树,同时也覆盖了 (i)。可以看看代码。

    由于最终答案不会超过 (3n),可以先将 (1)(n-1)(nxt_i+n)(min),这样相当于求出 ([n+1,2n)) 这一段向 (n) 贡献的答案。最后从 (n)(1) 依次更新即可。

    不过这题可以用单调队列做到 (mathcal O(n))。由于 ( m{KUN}) 值大的歌更容易停止,所以可以维护单调递减的队列,因为如果 ( m{KUN}) 值比自己大,下标还在自己后面,直接继承即可。

    代码

    #include <cstdio>
    #define print(x,y) write(x),putchar(y)
    
    template <class T>
    inline T read(const T sample) {
    	T x=0; char s; bool f=0;
    	while((s=getchar())>'9' or s<'0')
    		f |= (s=='-');
    	while(s>='0' and s<='9')
    		x = (x<<1)+(x<<3)+(s^48),
    		s = getchar();
    	return f?-x:x;
    }
    
    template <class T>
    inline void write(const T x) {
    	if(x<0) {
    		putchar('-');
    		write(-x);
    		return;
    	}
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    }
    
    #include <algorithm>
    using namespace std;
    
    const int maxn = 5e5+5;
    
    int n,t[maxn<<3],val[maxn];
    int nxt[maxn],res[maxn];
    struct node {
    	int id,ori,v;
    } s[maxn];
    
    inline int Min(int x,int y) {
    	return x<y?x:y;
    }
    
    void ins(int o,int l,int r,int p,int k) {
    	if(l==r) return t[o]=k,void();
    	int mid=l+r>>1;
    	if(p<=mid) ins(o<<1,l,mid,p,k);
    	else ins(o<<1|1,mid+1,r,p,k);
    	t[o] = Min(t[o<<1],t[o<<1|1]);
    }
    
    void build(int o,int l,int r) {
    	t[o] = (n<<1);
    	if(l==r) return;
    	int mid=(l+r)>>1;
    	build(o<<1,l,mid);
    	build(o<<1|1,mid+1,r);
    }
    
    int getLim(int x) {
    	x=(x-1>>1);
    	int r=upper_bound(val+1,val+n+1,x)-val-1;
    	if(!r) r=-1;
    	return r;
    }
    
    int ask(int o,int l,int r,int lim) {
    	if(r<=lim) return t[o];
    	int mid=l+r>>1;
    	int ret = ask(o<<1,l,mid,lim);
    	if(mid+1<=lim)
    		ret = Min(ret,ask(o<<1|1,mid+1,r,lim));
    	return ret;
    }
    
    int main() {
    	n=read(9);
    	for(int i=1;i<=n;++i)
    		val[i]=s[i].ori=read(9),s[i].id=i;
    	sort(s+1,s+n+1,[](const node &x,const node &y) {
    		return x.ori<y.ori;
    	});
    	for(int i=1;i<=n;++i) s[i].v=i;
    	sort(s+1,s+n+1,[](const node &x,const node &y) {
    		return x.id<y.id;
    	});
    	sort(val+1,val+n+1);
    	build(1,1,n);
    	for(int i=1;i<=n;++i)
    		ins(1,1,n,s[i].v,i);
    	int mid = getLim(s[1].ori);
    	if(~mid) nxt[1] = ask(1,1,n,mid);
    	else nxt[1] = -1;
    	for(int i=2;i<=n;++i) {
    		ins(1,1,n,s[i-1].v,i-1+n);
    		mid = getLim(s[i].ori);
    		if(~mid) nxt[i] = ask(1,1,n,mid);
    		else nxt[i] = -1;
    	}
    	bool infty = true; int ans=n*4;
    	for(int i=1;i<=n;++i)
    		if(~nxt[i]) {
    			infty = false;
    			break;
    		}
    	if(infty) {
    		for(int i=1;i<=n;++i)
    			print(-1,' ');
    		puts("");
    		return 0;
    	}
    	for(int i=1;i<n;++i)
    		if(~nxt[i])
    			ans = Min(ans,nxt[i]+n);
    	for(int i=n;i>=1;--i) {
    		if(~nxt[i])
    			ans = Min(ans,nxt[i]);
    		res[i] = ans-i;
    	}
    	for(int i=1;i<=n;++i)
    		print(res[i],' '); 
    	puts("");
    	return 0;
    }
    

    (sf CF1237E Balanced Binary Search Trees)

    解法

    首先可以发现,合法的树一定是两个高度相同的合法的树再加上根节点构成的,我们只需要根节点的编号奇偶性合法即可。

    手玩几组数据发现,树大小为 (1,5,9,...) 的树有且仅有一种合法的树的形态,而且它们均满足一个性质:给定序列构造这种树,序列首位的奇偶性与最终给出根节点的奇偶性相等,定义它们的集合为 (S)。同时,我们发现树大小为 (2,4,10,...) 的树有且仅有一种合法的树的形态,也满足类似的性质,只不过是奇偶性互异而已,定义它们的集合为 (T)

    由于需要子树深度相同,我们只能用 ((1,2),(5,4),(9,10)) 来构造新的树。

    我们发现,根节点的右儿子只能属于 (T) 才能保证奇偶性合法。同时,左儿子 (S,T) 都可以选。这样新的一层树显然也只有两种。而且,我们惊喜地发现,那两种方案正好分别属于 (S,T)!这可以从这两种方案的构造方法来证明。

    这样我们保存 (S,T) 的每一层的树的大小,就可以 (mathcal O(log n)) 递推了。

    代码

    #include <cstdio>
    #define print(x,y) write(x),putchar(y)
    
    template <class T>
    inline T read(const T sample) {
    	T x=0; char s; bool f=0;
    	while((s=getchar())>'9' or s<'0')
    		f |= (s=='-');
    	while(s>='0' and s<='9')
    		x = (x<<1)+(x<<3)+(s^48),
    		s = getchar();
    	return f?-x:x;
    }
    
    template <class T>
    inline void write(const T x) {
    	if(x<0) {
    		putchar('-');
    		write(-x);
    		return;
    	}
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    }
    
    int main() {
    	int n=read(9);
    	int a=2,b=1; bool ans = false;
    	while(a<=n or b<=n) {
    		if(a==n) {
    			ans = true; break;
    		}
    		if(b==n) {
    			ans = true; break;
    		}
    		int A = a+b+1;
    		int B = (a<<1)+1;
    		a=A,b=B;
    	}
    	print(ans,'
    ');
    	return 0;
    }
    

    (sf CF1237F Balanced Domino Placements)

    解法

    麻了,除了自己的所有人都会做。

    首先行和列可以拆开来做。最后再将它们配对即可,比如总共 (k) 个方块,选了 (k) 种行、列的位置,就是 (k!) 的。

    (dp_{i,j,k}) 为考虑前 (i) 行,放 (j) 个长度为 (2)(k) 个长度为 (1) 的块的方案数。然而状态就直接爆炸了。其实长度为 (1) 的块并不需要 (mathtt{dp}) 求解,只要直到剩多少位置给它放就可以用组合数计算。

    所以令 (dp_{i,j}) 为考虑前 (i) 行,放 (j) 个长度为 (2) 的块的方案数。转移见下。

    令行、列求出的数组分别为 (f,g)。枚举行、列选长度为 (2) 的块就可以暴力合并了。总复杂度 (mathcal O(n^2))

    代码

    #include <cstdio>
    #define print(x,y) write(x),putchar(y)
    
    template <class T>
    inline T read(const T sample) {
    	T x=0; char s; bool f=0;
    	while((s=getchar())>'9' or s<'0')
    		f |= (s=='-');
    	while(s>='0' and s<='9')
    		x = (x<<1)+(x<<3)+(s^48),
    		s = getchar();
    	return f?-x:x;
    }
    
    template <class T>
    inline void write(const T x) {
    	if(x<0) {
    		putchar('-');
    		write(-x);
    		return;
    	}
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    }
    
    #include <iostream>
    using namespace std;
    
    const int maxn = 3605;
    const int mod = 998244353;
    
    int n,m,k,cntr,cntc,lim;
    int f[maxn][maxn],g[maxn][maxn];
    int fac[maxn],ifac[maxn];
    bool row[maxn],col[maxn];
    
    inline int inc(int x,int y) {
    	return x+y>=mod?x+y-mod:x+y;
    }
    
    inline int mul(int x,int y) {
    	return 1ll*x*y%mod;
    }
    
    inline int inv(int x,int y=mod-2) {
    	int r=1;
    	while(y) {
    		if(y&1) r=1ll*r*x%mod;
    		x=1ll*x*x%mod; y>>=1;
    	}
    	return r;
    }
    
    void init() {
    	fac[0]=1;
    	for(int i=1;i<=lim;++i)
    		fac[i]=1ll*fac[i-1]*i%mod;
    	ifac[lim]=inv(fac[lim]);
    	for(int i=lim-1;i>=0;--i)
    		ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
    }
    
    int C(int n,int m) {
    	if(n<m or n<0 or m<0) return 0;
    	return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
    }
    
    int main() {
    	cntr=n=read(9),cntc=m=read(9),k=read(9);
    	for(int i=1;i<=k;++i) {
    		int x=read(9),y=read(9);
    		int a=read(9),b=read(9);
    		row[x]=row[a]=1;
    		col[y]=col[b]=1;
    		--cntr,--cntc;
    		if(x^a) --cntr;
    		if(y^b) --cntc;
    	}
    	lim = max(n,m);
    	init();
    	f[0][0]=g[0][0]=1;
    	for(int i=1;i<=n;++i)
    		for(int j=0;j<=(i>>1);++j) {
    			f[i][j]=f[i-1][j];
    			if(j and i>1 and !row[i] and !row[i-1])
    				f[i][j] = inc(f[i][j],f[i-2][j-1]);
    		}
    	for(int i=1;i<=m;++i)
    		for(int j=0;j<=(i>>1);++j) {
    			g[i][j]=g[i-1][j];
    			if(j and i>1 and !col[i] and !col[i-1])
    				g[i][j] = inc(g[i][j],g[i-2][j-1]);
    		}
    	int ans=0;
    	for(int i=0;i<=(cntr>>1);++i)
    		for(int j=0;j<=(cntc>>1);++j)
    			if(i*2+j<=cntr and i+j*2<=cntc)
    				ans = inc(ans,mul(f[n][i],mul(g[m][j],mul(C(cntr-i*2,j),mul(C(cntc-j*2,i),mul(fac[i],fac[j]))))));
    	print(ans,'
    ');
    	return 0;
    }
    
  • 相关阅读:
    BUUCTF RE 内涵的软件 WP
    BUUCTF RE reverse2 WP
    [笔记]Ubuntu使用遇到的问题及解决办法
    [笔记]Capybara API学习整理
    [笔记]Ruby On Rails创建项目改用Mysql数据库
    [笔记]Ubuntu12.04系统安装的一些工具
    [笔记]Ubuntu查看Windows创建的中文名文件乱码
    [笔记]BDD测试使用Capybara遇到的问题及解决方法
    [笔记]Capybara API学习整理(Ambiguous match, found N elements matching xpath)
    [笔记]How To Create a Gherkin Syntax Highlighter In gedit
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/15409122.html
Copyright © 2020-2023  润新知