noip模拟35 玩游戏 排列 最短路 矩形

玩游戏

考场思路

想了一个极为麻烦的贪心,但是没时间写了。

正解:

将左标记向左移,寻找每个左标记右标记最靠右的位置

代码实现
#include<bits/stdc++.h>
#define lt long long
#define int long long
using namespace std;
const lt N=100050;
int n,k,r,T;
int a[N];
int sum[N];
bool flag=0;

signed main(){
//	freopen("shuju.in","r",stdin);
//	freopen("my.out","w",stdout);
	scanf("%lld",&T);
	for(int w=1;w<=T;++w){
		memset(sum,0,sizeof(sum));
		flag=0;
		scanf("%lld%lld",&n,&k);
		r=k;
		for(int i=1;i<=n;i++){
			scanf("%lld",&a[i]);
		}
		for(int j=k-1;j>=1;--j){
			sum[j]=sum[j+1]+a[j+1];
		}
		for(int j=k+1;j<=n;++j){
			sum[j]=sum[j-1]+a[j];
		}
//		cout<<r<<endl;
//		for(int i=1;i<=n;++i){
//			cout<<sum[i]<<" |";
//		}
		for(int j=k;j>=1;--j){
			while(sum[r+1]+sum[j]<=0&&r<n) r++;
			while(sum[r]+sum[j]>0&&r>0) r--;
			if(r<k){
				flag=1;
				break;
			}
		}
//		cout<<r<<endl;
		if(flag==0&&r==n){
			printf("Yes\n");
		}
		else{
			printf("No\n");
		}
	}
	return 0;
}

排列

考场思路:

通过k=1的点骗了些分,想到了k不可能大于logN,但是输出是零的这一块并没有设置数据点。

正解

dp题
对于一个大区间,可以被区间内最大的值分为两个小区间,两个小区间是相互独立的互不干扰的,所以一个大区间的方案数是两个小区间的方案数乘组合数(因为是从大区间随意选数进入第一个区间,剩下的进入第二个区间)。
对于每一个区间都有四种情况,左边是挨着一个更大数还是靠墙,右边是挨着一个更大数还是靠墙,设置dp数组f[i][j][1/0][1/0],i表示区间长度,j表示操作几次,并且处理为,从操作一次到操作j次的前缀和(记住,后面会用到多次),后面两维分别表示左右两边是否靠墙。
考虑转移,对于00的情况(意为两边都靠墙)可以由j相同的左小区间01和右小区间10转移过来,因为对于两边都靠墙的情况,当两边的小区间消除完之后,他本身也就结束了
对于10的情况我们如果想让它在次内完成操作,就必须让区间内的最大值在j-1次的时候与左边界相遇,才能使得在第j次时被消除,所以由左边的j-1的11和右边的j的10转移过来
01同理
对于11的情况,我们可以让中间值在j-1的时候靠左或靠右,
因为j代表的是前缀和,所以我们只要先由两边都是j转移过来,再将两边恰好同时选j的情况减去即可

代码实现
#include<bits/stdc++.h>
#define lt long long
using namespace std;
const lt N=1005;
lt n,kk,mod;
lt f[N][100][2][2],c[N][N];

int main(){
	scanf("%lld%lld%lld",&n,&kk,&mod);
	for(int i=0;i<=kk+1;++i){
		f[0][i][0][0]=f[0][i][0][1]=f[0][i][1][0]=f[0][i][1][1]=1;
	}
	
	for(int i=0;i<=n;i++){
		c[i][0]=1;
		for(int j=1;j<=i;j++){
			c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
//			cout<<c[i][j]<<"|";
		}
//		cout<<endl;
	}
	for(int i=1;i<=n;++i){
		for(int k=1;k<=kk+1;k++){
			for(int j=1;j<=i;++j){
			
f[i][k][0][0]+=((f[j-1][k][0][1]*f[i-j][k][1][0])%mod*c[i-1][j-1])%mod;
f[i][k][0][1]+=((f[j-1][k][0][1]*f[i-j][k-1][1][1])%mod*c[i-1][j-1])%mod;
f[i][k][1][0]+=((f[j-1][k-1][1][1]*f[i-j][k][1][0])%mod*c[i-1][j-1])%mod;
f[i][k][1][1]+=(((f[j-1][k][1][1]*f[i-j][k][1][1])-(f[j-1][k][1][1]-f[j-1][k-1][1][1])*(f[i-j][k][1][1]-f[i-j][k-1][1][1]))%mod*c[i-1][j-1])%mod;

			f[i][k][0][0]%=mod;
			f[i][k][0][1]%=mod;
			f[i][k][1][0]%=mod;
			f[i][k][1][1]%=mod;
	//		printf("%d %d %d\n",i,k,f[i][k][0][0]);
			}
		}
	}
	lt ens=((f[n][kk][0][0]-f[n][kk-1][0][0])%mod+mod)%mod;
	printf("%lld\n",ens);
}

(最短路暂咕咕咕)

矩形

考场思路

在考场上想到了正解,但数组开小了所以只得了50分,考完之后直接10倍数组就过了(到现在都不明白为什么四倍数组都过不去)

正解:

(其实是我的想法,正解又难写又慢)(而且还看不懂)
从左往右扫,遇到左边界便加进线段树中,搜索线段树中的这一段区间,将线段树中将线段树的这一部分点中的标号的矩形与当前矩形连边,并且标成当前矩形。当到达右边界,删除矩形时,如果线段树的这一部分只剩一层矩形,则直接删除;如果还有其他矩形,则只将矩形的层数减一即可,不必修改这一部分的标号(因为它和剩余矩形肯定是相交的,所以两者等价)

代码实现
#include<bits/stdc++.h>
using namespace std;
const int N=1000050;
int n,tot,cnt;
int x[N],y[N],xx[N],yy[N],head[N];

int t[N*4],sum[N*4],co[N*4],lz[N*4];
bool clen[N*4],vis[N];//记得消除

int xmx,ymx;
vector<int> in[N],out[N];

struct edge{
	int fr,nxt,to;
}e[N*8];//记得双向建边

void ade(int x,int y){
	e[++tot].to=y;
	e[tot].fr=x;
	e[tot].nxt=head[x];
	head[x]=tot;
}

void pushdown(int u){
	if(clen[u]){
		t[u<<1]=0;
		t[u<<1|1]=0;
		clen[u<<1]=1;
		clen[u<<1|1]=1;
		co[u<<1]=co[u];
		co[u<<1|1]=co[u];
		clen[u]=0;
	}
	if(lz[u]){
		lz[u<<1]+=lz[u];
		lz[u<<1|1]+=lz[u];
		sum[u<<1]+=lz[u];
		sum[u<<1|1]+=lz[u];
		lz[u]=0;
	}
		if(sum[u<<1]==0){
			co[u<<1]=0;
			t[u<<1]=0;
		}
		if(sum[u<<1|1]==0){
			co[u<<1|1]=0;
			t[u<<1|1]=0;
		}
//	if(u==5){
//		printf("%d %d %d %d %d" ,lz[u],co[u],clen[u],co[u<<1],co[u<<1|1]);
//	}
}

void pushup(int u){
	if(co[u<<1]!=co[u<<1|1]&&co[u<<1]&&co[u<<1|1]){
		co[u]=0;
		t[u]=1;
	}
	if(!co[u<<1]&&co[u<<1|1]){
		co[u]=co[u<<1|1];
	}
	if(!co[u<<1|1]&&co[u<<1]){
		co[u]=co[u<<1];
	}
	if(co[u<<1]==co[u<<1|1]){
		co[u]=co[u<<1|1];
	}
	sum[u]=max(sum[u<<1],sum[u<<1|1]);
	if(t[u<<1]||t[u<<1|1]){
		t[u]=1;
	}
}

void ask(int u,int l,int r,int ll,int rr,int x){
	if(l>rr||r<ll){
		return;
	}
//	if(1){
//		printf("[%d %d %d %d %d]\n",l,r,u,t[u],co[u]);
//	}
	if(l>=ll&&r<=rr){
		if(!t[u]){
			if(co[u]){
				ade(co[u],x);
				ade(x,co[u]);
			}
		}
		else{
			pushdown(u);
			int mid=(l+r)>>1;
			ask(u<<1,l,mid,ll,rr,x);
			ask(u<<1|1,mid+1,r,ll,rr,x);
		}
		return;
	}
	pushdown(u);
	int mid=(l+r)>>1;
	ask(u<<1,l,mid,ll,rr,x);
	ask(u<<1|1,mid+1,r,ll,rr,x);
}

void edit(int u,int l,int r,int ll,int rr,int x){
	if(l>rr||r<ll){
		return;
	}
	if(l>=ll&&r<=rr){
		t[u]=0;
		clen[u]=1;
		sum[u]+=1;
		lz[u]+=1;
		co[u]=x;
		return;
	}
	pushdown(u);
	int mid=(l+r)>>1;
	edit(u<<1,l,mid,ll,rr,x);
	edit(u<<1|1,mid+1,r,ll,rr,x);
	pushup(u);
//	if(1){
//		printf("<%d %d %d %d %d>\n",l,r,u,t[u],co[u]);
//	}
	
}

void del(int u,int l,int r,int ll,int rr){
	if(l>rr||r<ll){
		return;
	}
	if(l>=ll&&r<=rr){
		if(sum[u]==1){
			sum[u]--;
			co[u]=0;
			t[u]=0;
			lz[u]--;
		}
		else{
			sum[u]--;
			lz[u]--;
		}
		return;
	}
	pushdown(u);
	int mid=(l+r)>>1;
	del(u<<1,l,mid,ll,rr);
	del(u<<1|1,mid+1,r,ll,rr);
	pushup(u);
}

void dfs(int u){
	vis[u]=1;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(!vis[v]){
			dfs(v);
		}
	}
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d%d%d%d",&x[i],&y[i],&xx[i],&yy[i]);
		xmx=max(xx[i],xmx);
		ymx=max(yy[i],ymx);
		in[x[i]].push_back(i);
		out[xx[i]].push_back(i);
	}
//	cout<<endl;
	for(int i=1;i<=xmx;i++){
		int sze=in[i].size();
		for(int j=0;j<sze;++j){
//			printf("%d %d %d %d \n",i,in[i][j],y[in[i][j]],yy[in[i][j]]);
			ask(1,1,ymx,y[in[i][j]],yy[in[i][j]],in[i][j]);
			edit(1,1,ymx,y[in[i][j]],yy[in[i][j]],in[i][j]);
		}
		int sz=out[i].size();
		for(int j=0;j<sz;++j){
			int ot=out[i][j];
			del(1,1,ymx,y[ot],yy[ot]);
		}
	}
//	for(int i=1;i<=tot;++i){
//		cout<<e[i].fr<<" "<<e[i].to<<endl;
//	}
	for(int i=1;i<=n;++i){
		if(!vis[i]){
			++cnt;
			dfs(i);
		}
	}
	cout<<cnt<<endl;
	return 0;
}

noip模拟35 玩游戏 排列 最短路 矩形

上一篇:Centos7 从 git version 1.8.3.1升级git version 2.32.0 全过程


下一篇:机器学习-dataset、dataloader的使用(pytorch环境)