noip模拟5

T1

题目描述:
给定一个由小写字母组成的字符串 \(s\)。有 \(m\) 次操作,每次操作给定 3 个参数 \(l\),\(r\),\(x\)。如果 \(x\)=1,将 \(s[l]\) ~ \(s[r]\)升序排序;如果 \(x\)=0,将 \(s[l]\) ~ \(s[r]\)降序排序。你需要求出最终序列。


考试时想到可以用线段树维护了,但是蒟弱实属不会实现,于是就打了一发暴力sort,骗得40而归~

考虑正解吧。用一颗维护26个值的线段树,实现区间的修改与查询,最后单点查询输出即可。

坑点:

  • 既然是区间修改问题,那么懒标记在大部分时候都要派上用场,不要忘记下放;

  • 本来想节省点空间开\(son[26]\)呢,忘了'a'-'a'==0这茬,所以在调试时吃了大亏,和T4一样,特特特别感谢各位同机房大佬们对蒟弱的大力支持。

好了,开始讨论。每次面对一个指令,我们先使用桶排序储存区间内每个数的个数,然后利用桶排序的分类特性将各个数正序或倒序放回线段树即可。

利用lz懒标记来储存本区间内将要修改成的数,然后就是简单的线段树板子惹:

Code

#include<bits/stdc++.h>
using namespace std;
namespace EMT{
	int read(){int x=0;char ch=getchar();while(ch<'0'||ch>'9')ch=getchar();while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x;}
	#define F(i,a,b) for(register int i=a;i<=b;i++)
	#define pf printf
	void pi(int x){pf("%d ",x);}void pn(){pf("\n");}void ps(int x[],int size){F(i,1,size)pi(x[i]);pn();}void pt(){pf(" ");}
	const int N=1e5+100;
	int n,m,a[N],cnt[26];char s[N];struct tree{int l,r,sum[26],lz;}t[N<<2];
	inline void up(int p){F(i,0,25)t[p].sum[i]=t[p*2].sum[i]+t[p*2+1].sum[i];}
	inline void down(int p){
		if(t[p].lz!=-1){
			int k=t[p].lz;t[p].lz=-1;
			t[p*2].lz=t[p*2+1].lz=k;
			F(i,0,25)t[p*2].sum[i]=t[p*2+1].sum[i]=0;
			t[p*2].sum[k]+=t[p*2].r-t[p*2].l+1;
			t[p*2+1].sum[k]+=t[p*2+1].r-t[p*2+1].l+1;
		}
	}
	inline void build(int p,int l,int r){
		t[p].l=l,t[p].r=r;t[p].lz=-1;
		if(l==r){t[p].sum[a[l]]++;return;}
		int mid=(l+r)>>1;
		build(p*2,l,mid);build(p*2+1,mid+1,r);
		up(p);
	}
	inline void ask(int p,int l,int r){
		if(t[p].l>=l&&t[p].r<=r){F(i,0,25)cnt[i]+=t[p].sum[i];return;}
		down(p);
		int mid=(t[p].l+t[p].r)>>1;
		if(l<=mid)ask(p*2,l,r);
		if(r>mid)ask(p*2+1,l,r);
	}
	inline int Ask(int p,int x){
		if(t[p].l==t[p].r){
			F(i,0,25)if(t[p].sum[i])return i;
		}down(p);
		int mid=(t[p].l+t[p].r)>>1;
		if(x<=mid)return Ask(p*2,x);
		else return Ask(p*2+1,x);
	}
	inline void change(int p,int l,int r,int w){
		if(l>r)return;
		if(t[p].l>=l&&t[p].r<=r){
			F(i,0,25)t[p].sum[i]=0;
			t[p].sum[w]=t[p].r-t[p].l+1;
			t[p].lz=w;return;
		}
		down(p);
		int mid=(t[p].l+t[p].r)>>1;
		if(l<=mid)change(p*2,l,r,w);
		if(r>mid)change(p*2+1,l,r,w);
		up(p);
	}
	inline short main(){
		n=read();m=read();scanf("%s",s+1);F(i,1,n)a[i]=s[i]-'a';
		build(1,1,n);
		F(T,1,m){
			memset(cnt,0,sizeof(cnt));
			int l=read(),r=read(),x=read();
			ask(1,l,r);
			if(x){
				int last=l;
				F(i,0,25)if(cnt[i]){change(1,last,last+cnt[i]-1,i);last+=cnt[i];}
			}
			else{
				int last=l;
				for(register int i=25;i>=0;i--)if(cnt[i]){change(1,last,last+cnt[i]-1,i);last+=cnt[i];}
			}
		}
		F(i,1,n)pf("%c",Ask(1,i)+'a');return 0;
	}
}
signed main(){return EMT::main();}

T2 matrix

题目:

求出满足以下条件的 \(n*m\) 的 01 矩阵个数:
(1)第 i 行第 1~\(li\) 列恰好有 1 个 1。 (\(li\)+1到\(ri\)-1不能放1) (2)第 \(i\) 行第 \(ri\)~\(m\) 列恰好有 1 个 1。
(3)每列至多有 1 个 1。


暴搜才骗到20分,差评。分列进行枚举

\(f[i][j]\)表示目前dp到第i列,有j个右区间的点放在了i列左端,

考虑两种情况:

1.这一列不放,那么\(f[i][j]=f[i-1][j]\).

2.这一列放,那么有\(r[i]-(j-1)\)个点可供挑选,\(f[i][j]=f[i-1][j-1]*(r[i]-(j-1))\).

举个例子,一共要选x列放1,其中对于第p行和第q行,都在第o列放一,当然不是同一种情况,所以要考虑乘上一个排列,\(f[i][j]*=_{l[i]-l[i-1]}^{i-j-l[i-1]}\textrm{A}\)

其中,\(l[i]\)\(r[i]\)分别表示左区间在i及i列以左的区间总数和右区间在i列及其以左的区间总数。

最后的答案就是f[m][n]

Code

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio>
using namespace std;
namespace EMT{
	int max(int a,int b){return a>b?a:b;}
	int min(int a,int b){return a<b?a:b;}
	int read(){int x=0;char ch=getchar();while(ch<'0'||ch>'9')ch=getchar();while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x;}
	#define pf printf
	#define F(i,a,b) for(register int i=a;i<=b;i++)
	#define int long long
	inline void pi(int x){pf("%lld ",x);}inline void pt(){pf(" ");}inline void pn(){pf("\n");}inline void ps(int x[],int size){F(i,1,size)pi(x[i]);pn();}
	const int N=3005,mod=998244353;
	int tot,n,m,l[N],r[N],f[N][N];bool use[N];
	signed main(){
		n=read();m=read();if(2*n>m){pi(0);return 0;}
		F(i,1,n){int x=read(),y=read();l[x]++;r[y]++;}
		f[0][0]=1;
		F(i,1,m)l[i]+=l[i-1],r[i]+=r[i-1];
		F(i,1,m){
			f[i][0]=f[i-1][0];
			F(j,1,i)f[i][j]=(f[i-1][j]+f[i-1][j-1]*(r[i]-j+1))%mod;
			F(j,l[i-1],l[i]-1)F(k,0,i)f[i][k]=f[i][k]*(i-j-k)%mod;
		}
		pi(f[m][n]);
		return 0;
	}
}
signed main(){return EMT::main();}

T3 big

考试时想到建0/1 trie树了,也考虑到先后左移的问题了,就是不知道一开始要把什么东西投进去,没想到是个0。。。

首先考虑对手什么时候操作。

\(x\)异或之后再左移1位,等价于将之前异或过的数都左移1位,再直接异或\(x\)左移1位。

所以可以预处理出所有的左移序列投到trie树上。

然后考虑子结点对自己的影响。

\(now\)为遍历到某个点时得到的值,

如果有0/1两个子节点,那么对手一定会选择不让你的值增大,也就是说无法对\(now\)产生贡献,now<<=1;

如果只有1个,那么我们一定能得到使该点产生贡献的数(因为任选),
即now=(now<<1)+1

如果没有,说明遍历完毕,记录更新maxn和cnt。

Code



#include<bits/stdc++.h>
using namespace std;
namespace EMT{
	int read(){int x=0;char ch=getchar();while(ch<'0'||ch>'9')ch=getchar();while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x;}
	#define int long long
	#define F(i,a,b) for(register int i=a;i<=b;i++)
	#define pf printf
	void pi(int x){pf("%lld ",x);}void pn(){pf("\n");}void ps(int x[],int size){F(i,1,size)pi(x[i]);pn();}void pt(){pf(" ");}
	struct tree{
		int son[2];
	}tir[100000000];int num,maxx=-1,cnt,n,m;
	int w(int x){
		int cntt=0;
		while(x){
			x-=x&(-x);
			cntt++;
		}return cntt;
	}
	void ins(int k){
		int now=0;
		for(int i=n-1,j;i>=0;i--){
			j=(k>>i)&1;
			if(!tir[now].son[j])tir[now].son[j]=++num;
			now=tir[now].son[j];
		}
	}const int N=1e5+100;
	int a[N];
	int ope(int x){return (((2*x)/(1<<n))+2*x)%(1<<n);}
	void dfs(int now,int tot){
		if(tir[now].son[0]&&tir[now].son[1]){
			dfs(tir[now].son[0],tot<<1);
			dfs(tir[now].son[1],tot<<1);
			return;
		}
		if(tir[now].son[0]&&!tir[now].son[1]){
			dfs(tir[now].son[0],(tot<<1)+1);
			return;
		}
		if(!tir[now].son[0]&&tir[now].son[1]){
			dfs(tir[now].son[1],(tot<<1)+1);
			return;
		}
		if(maxx==tot)cnt++;
		if(maxx<tot)maxx=tot,cnt=1;
	}
	signed main(){
		n=read();m=read();
		F(i,1,m)a[i]=read()^a[i-1];
		F(i,0,m){int x=a[m]^a[i];ins(x^ope(a[i]));}
		dfs(0,0);pi(maxx);pi(cnt);
	}
}
signed main(){return EMT::main();}

T4 所驼门王的宝藏

题目描述:

图片.png

千算万算没想到,竟然是sort比较函数出了锅。。。

有向图,我们考虑将有用的点用tarjan缩成几个强联通分量,利用拓扑排序来求得ans。

首先建边是个难题。分行、列、周围建边,由于一行中的横天门只要到达一个就能到达所有,所以只需要每个门和本行另外一条门联通即可。

对于行:

inline bool xcom(point a,point b){
		if(a.x!=b.x)return a.x<b.x;
		if(a.kd==b.kd&&a.kd==1)return a.y<b.y;
		if(a.kd==1)return 1;if(b.kd==1)return 0;
		return a.y<b.y;
}

inline void opex(){
	sort(p+1,p+n+1,xcom);
	int first=1,last=1;
	for(register int i(1);i<=n;++i){
		if(p[i].x!=p[i+1].x){
			if(first!=last)
				add(p[last].id,p[first].id); 
			first=last=i+1;
		}
		else{
			if(p[last].kd==1)add(p[last].id,p[i+1].id);
			if(p[i+1].kd==1)last=i+1; 
			if(p[first].kd!=1)last=first=i+1; 
		}
	}
}

对于列:


inline bool ycom(point a,point b){
	if(a.y!=b.y)return a.y<b.y;
	if(a.kd==b.kd&&a.kd==2)return a.y<b.y;
	if(a.kd==2)return 1;if(b.kd==2)return 0;
	return a.y<b.y;
}

inline void opey(){
	sort(p+1,p+n+1,ycom);
	int first=1,last=1;
	for(register int i(1);i<=n;++i){
		if(p[i].y!=p[i+1].y){
			if(first!=last)add(p[last].id,p[first].id);
			first=last=i+1;
		}
		else{
			if(p[last].kd==2)add(p[last].id,p[i+1].id);
			if(p[i+1].kd==2)last=i+1;
			if(p[first].kd!=2)last=first=i+1;
		}
	}
}                							

对于周围:


int X[10]={0,1,1,0,0,1,-1,-1,-1};
int Y[10]={0,0,1,1,-1,-1,1,0,-1};
inline void opez(){
	F(i,1,n){
		if(p[i].kd==3){
			F(j,1,8)if(hh.count(pa(p[i].x+X[j],p[i].y+Y[j])))add(p[i].id,hh[pa(p[i].x+X[j],p[i].y+Y[j])]);
		}
	}
}

建完边,跑完tj后就可以愉快拓扑了~

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<map>
#include<stack>
#include<queue>
#include<algorithm>
using namespace std;
namespace EMT{
	int read(){int x=0;char ch=getchar();while(ch<'0'||ch>'9')ch=getchar();while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x;}
	const int N=1e5+10;int cntk,n,co,head[N],pos[N],dfn[N],low[N],dp[N],num,in[N],size[N];stack<int>s;queue<int>q;bool ins[N];
	struct point{int id,x,y,kd;}p[N];struct node{int next,from,to;}e[N<<4];
	map<pair<int,int>,int>hh;
	void add(int next,int to){e[++co].next=head[next],e[co].from=next,e[co].to=to,head[next]=co;}
	inline bool xcom(point a,point b){
		if(a.x!=b.x)return a.x<b.x;
		if(a.kd==b.kd&&a.kd==1)return a.y<b.y;
		if(a.kd==1)return 1;if(b.kd==1)return 0;
		return a.y<b.y;
	}
	inline bool ycom(point a,point b){
		if(a.y!=b.y)return a.y<b.y;
		if(a.kd==b.kd&&a.kd==2)return a.y<b.y;
		if(a.kd==2)return 1;if(b.kd==2)return 0;
		return a.y<b.y;
	}
	inline void opex(){
		sort(p+1,p+n+1,xcom);
		int first=1,last=1;
		for(register int i(1);i<=n;++i){
			if(p[i].x!=p[i+1].x){
				if(first!=last)
					add(p[last].id,p[first].id); 
				first=last=i+1;
			}
			else{
				if(p[last].kd==1)add(p[last].id,p[i+1].id);
				if(p[i+1].kd==1)last=i+1; 
				if(p[first].kd!=1)last=first=i+1; 
			}
		}
	}
	inline void opey(){
		sort(p+1,p+n+1,ycom);
		int first=1,last=1;
		for(register int i(1);i<=n;++i){
			if(p[i].y!=p[i+1].y){
				if(first!=last)add(p[last].id,p[first].id);
				first=last=i+1;
			}
			else{
				if(p[last].kd==2)add(p[last].id,p[i+1].id);
				if(p[i+1].kd==2)last=i+1;
				if(p[first].kd!=2)last=first=i+1;
			}
		}
	}
	int X[10]={0,1,1,0,0,1,-1,-1,-1};
	int Y[10]={0,0,1,1,-1,-1,1,0,-1};
	inline void opez(){
		F(i,1,n){
			if(p[i].kd==3){
				F(j,1,8)if(hh.count(pa(p[i].x+X[j],p[i].y+Y[j])))add(p[i].id,hh[pa(p[i].x+X[j],p[i].y+Y[j])]);
			}
		}
	}
	inline void tj(int x){
		dfn[x]=low[x]=++num;
		s.push(x),ins[x]=1;
		for(register int i(head[x]),j;i;i=e[i].next){
			j=e[i].to;
			if(!dfn[j])tj(j),low[x]=min(low[x],low[j]);
			else if(ins[j])low[x]=min(low[x],dfn[j]);
		}
		if(dfn[x]==low[x]){
			++cntk;int y;
			do{
			y=s.top(),s.pop(),ins[y]=0;
			pos[y]=cntk,++size[cntk];
			}while(x!=y);
		}
	}
	int main(){
		n=read(),read(),read();
		for(register int i(1);i<=n;++i){p[i].x=read(),p[i].id=i,p[i].y=read(),p[i].kd=read(),hh[pair<int,int>(p[i].x,p[i].y)]=i;}
		opex(),opey(),opez();
		for(register int i(1);i<=n;++i)if(!dfn[i])tj(i);
		hh.clear();int Co=co;co=0;memset(head,0,sizeof(head));
		for(register int i(1);i<=Co;++i){
			int x=e[i].from,y=e[i].to;
			if(pos[x]!=pos[y])add(pos[x],pos[y]),++in[pos[y]];
		}int ans(0);
		for(register int i(1);i<=cntk;++i){if(!in[i])q.push(i);dp[i]=size[i];}
		while(!q.empty()){
			int u=q.front();q.pop();
			for(register int i=head[u],j;i;i=e[i].next){
				j=e[i].to,in[j]--;
				dp[j]=dp[j]>dp[u]+size[j]?dp[j]:dp[u]+size[j];
				if(!in[j])q.push(j);
			}
		}
		for(register int i(1);i<=cntk;++i)ans=ans>dp[i]?ans:dp[i];
		printf("%d\n",ans);return 0;
	}
}
int main(){return EMT::main();}
posted @ 2021-06-08 11:23  letitdown  阅读(72)  评论(1编辑  收藏  举报