ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

8.10考试总结(NOIP模拟35)[玩游戏·排列·最短路·矩形]

2021-08-12 19:34:35  阅读:181  来源: 互联网

标签:NOIP int long flag 35 define pos2 8.10 pos1


T1 玩游戏

解题思路

可以把序列从 k 位置掰成两个序列。

问题就变成了两个序列从开头走向末尾是否可以保证前缀和之和一直不大于 0 。

并且可以移动到两个序列的末尾,问题就变成处理前缀和。

然后在每一个序列里维护一个 next 值,表示可以跳到的较小值。

这里需要正反扫一遍,毕竟只扫一边的话会有最小值一边的 next 无法更新。

然后就是对于两个序列分别从两边扫一边。

看看是否可以跳到对应的 next 位置。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=1e5+10,INF=1e18;
int T,n,k,l,r,sum,s[N],nxt1[N],nxt2[N];
int a[N],b[N],cnt1,cnt2,pre1[N],pre2[N];
void solve()
{
	memset(nxt1,0,sizeof(nxt1));
	memset(nxt2,0,sizeof(nxt2));
	n=read();	k=read();
	cnt1=cnt2=0;
	for(int i=1;i<=n;i++)
		s[i]=read();
	a[++cnt1]=0;	b[++cnt2]=0;
	for(int i=k+1;i<=n;i++)
		a[++cnt1]=s[i];
	for(int i=k;i>1;i--)
		b[++cnt2]=s[i];
	for(int i=1;i<=cnt1;i++)
		pre1[i]=pre1[i-1]+a[i];
	for(int i=1;i<=cnt2;i++)
		pre2[i]=pre2[i-1]+b[i];
	int pos,minn;
	minn=pre1[1];	pos=1;
	for(int i=2;i<=cnt1;i++)
		if(minn>=pre1[i])
		{
			nxt1[pos]=i;
			pos=i;
			minn=pre1[i];
		}
	minn=pre1[cnt1];	pos=cnt1;
	for(int i=cnt1-1;i>=1;i--)
		if(minn>pre1[i])
		{
			nxt1[pos]=i;
			pos=i;
			minn=pre1[i];
		}
	minn=pre2[1];	pos=1;
	for(int i=2;i<=cnt2;i++)
		if(minn>=pre2[i])
		{
			nxt2[pos]=i;
			pos=i;
			minn=pre2[i];
		}
	minn=pre2[cnt2];	pos=cnt2;
	for(int i=cnt2-1;i>=1;i--)
		if(minn>pre2[i])
		{
			nxt2[pos]=i;
			pos=i;
			minn=pre2[i];
		}
	if(pre1[cnt1]+pre2[cnt2]>0)
	{
		printf("No\n");
		return ;
	}
	int pos1=1,pos2=1;
	bool flag=true;
	while(nxt1[pos1]||nxt2[pos2])
	{
		flag=true;
		for(int i=pos1+1;i<=nxt1[pos1];i++)
			if(pre1[i]+pre2[pos2]>0)
			{
				flag=false;
				break;
			}
		if(!nxt1[pos1])	flag=false;
		if(flag){pos1=nxt1[pos1];continue;}
		flag=true;
		for(int i=pos2+1;i<=nxt2[pos2];i++)
			if(pre1[pos1]+pre2[i]>0)
			{
				flag=false;
				break;
			}
		if(!nxt2[pos2])	flag=false;
		if(!flag)	break;
		pos2=nxt2[pos2];
		flag=true;
	}
	if(!flag)
	{
		printf("No\n");
		return ;
	}
	pos1=cnt1;pos2=cnt2;
	while(nxt1[pos1]||nxt2[pos2])
	{
		flag=true;
		for(int i=pos1-1;i>=nxt1[pos1];i--)
			if(pre1[i]+pre2[pos2]>0)
			{
				flag=false;
				break;
			}
		if(!nxt1[pos1])	flag=false;
		if(flag){pos1=nxt1[pos1];continue;}
		flag=true;
		for(int i=pos2-1;i>=nxt2[pos2];i--)
			if(pre1[pos1]+pre2[i]>0)
			{
				flag=false;
				break;
			}
		if(!nxt2[pos2])	flag=false;
		if(!flag)	break;
		pos2=nxt2[pos2];
		flag=true;
	}
	if(!flag)
	{
		printf("No\n");
		return ;
	}
	printf("Yes\n");
}
signed main()
{
	T=read();
	while(T--)	solve();
	return 0;
}

排列

解题思路

动态规划。

DP 数组 \(f_{i,j,0/1,0/1}\) 表示区间长度为 i 操作 j 次正好可以只剩下一个的方案数。

0 或者 1 分别表示左边或者右边是否有更大的或者边界。

然后就可以通过之前的状态和组合数进行转移。

分别枚举现在区间的长度,操作次数,子区间长度,以及子区间操作次数进行转移。

转移的时候就好像在两个子区间中间插进去一个更大的数,其实是和挨着边界差不多的。

但是这样显然会 TLE 因此需要前缀和优化。

转移也是差不多的,只不过省掉了一维枚举子区间的操作。

然后直接整阶乘进行组合数的计算是会被卡常的,因此需要杨辉三角处理。。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e3+10;
int n,m,mod,c[N][N],f[N][15][2][2];
void init()
{
	c[0][0]=1;
	for(int i=1;i<=n;i++)
	{
		c[i][0]=c[i][i]=1;
		for(int j=1;j<i;j++)
			c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
	}
}
signed main()
{
	n=read();	m=read();	mod=read();
	init();
	for(int i=0;i<=m;i++)
		f[0][i][0][0]=f[0][i][1][0]=f[0][i][0][1]=f[0][i][1][1]=1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int k=1;k<=i;k++)
			{
				f[i][j][0][0]=(f[i][j][0][0]+f[k-1][j][0][1]*f[i-k][j][1][0]%mod*c[i-1][k-1])%mod;
				f[i][j][1][0]=(f[i][j][1][0]+f[k-1][j][1][0]*f[i-k][j-1][1][1]%mod*c[i-1][k-1])%mod;
				f[i][j][0][1]=(f[i][j][0][1]+f[k-1][j][0][1]*f[i-k][j-1][1][1]%mod*c[i-1][k-1])%mod;
				int tmp1=(f[k-1][j][1][1]-f[k-1][j-1][1][1]+mod)%mod;
				int tmp2=(f[i-k][j][1][1]-f[i-k][j-1][1][1]+mod)%mod;
				f[i][j][1][1]=(f[i][j][1][1]+(f[k-1][j][1][1]*f[i-k][j][1][1]%mod-tmp1*tmp2%mod+mod)*c[i-1][k-1])%mod;
			}
	printf("%lld",(f[n][m][0][0]-f[n][m-1][0][0]+mod)%mod);
	return 0;
}

最短路

解题思路

考场上是想了一个假做法,先从 1 为源点跑一边,用 bitset 进行维护。

然后再第一次的 Dij 的基础上继承以后再跑一次,更新答案。

然后这个做法是 WA 了两个点,但是由于测试是 subtask 的,所以就 20pts 了。

Yubai 的思路非常的妙!!

对于每一个有的边建一条反边,跑 二维DIJ。

\(dis_{i,j}\) 表示 从 1 节点沿正边到达 i 点以及沿反边到 j 点的距离之和。

这样 \(dis_{n,n}\) 表示的就是从 1 沿正边到 n 在沿正边走回来的距离。

同样用 bitset 维护,因为这个是动态的,因此具有正确性。

二维Dij 的时候注意两步不可以同时进行要一步一步来。。

通俗来讲就是两个循环而不是两层循环。。。

code

20ptsWA两个点

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=260,M=N*N,INF=1e18;
int n,m,ans=INF,s[N],dis[N],dis2[N];
int tot=1,head[N],nxt[M],ver[M],edge[M];
bitset<N> bit[N],bit2[N];
bool vis[N],b[M];
priority_queue<pair<int,int> > q;
void add_edge(int x,int y)
{
	ver[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
void check(int x)
{
	b[x]=true;
	for(int i=head[x];i;i=nxt[i])
		if(!b[ver[i]]&&vis[ver[i]])
			check(ver[i]);
}
bool judge()
{
	bool jud1,jud2;
	check(1);	jud1=b[n];
	memset(b,false,sizeof(b));
	check(n);	jud2=b[1];
	memset(b,false,sizeof(b));
	return jud1&&jud2;
}
void Dij()
{
	memset(dis,0x3f,sizeof(dis));
	dis[1]=s[1]; bit[1][1]=true;
	q.push(make_pair(-dis[1],1));
	while(!q.empty())
	{
		int x=q.top().second;q.pop();
		if(vis[x])	continue;
		vis[x]=true;
		for(int i=head[x];i;i=nxt[i])
		{
			int to=ver[i],sum=0;
			bitset<N> bi=bit[to]|bit[x];
			for(int j=1;j<=n;j++)
				if(bi[j])
					sum+=s[j];
			if(!bi[to])
			{
				bi[to]=true;
				sum+=s[to];
			}
			if(sum<dis[to])
			{
				dis[to]=sum;
				bit[to]=bi;
				q.push(make_pair(-dis[to],to));
			}
		}
	}
}
void Dij2()
{
	memset(dis2,0x3f,sizeof(dis2));
	dis2[n]=dis[n]; bit2[n]=bit[n];
	q.push(make_pair(-dis2[n],n));
	while(!q.empty())
	{
		int x=q.top().second;q.pop();
		if(vis[x])	continue;
		vis[x]=true;
		for(int i=head[x];i;i=nxt[i])
		{
			int to=ver[i],sum=0;
			bitset<N> bi=bit2[to]|bit2[x];
			for(int j=1;j<=n;j++)
				if(bi[j])
					sum+=s[j];
			if(!bi[to])
			{
				bi[to]=true;
				sum+=s[to];
			}
			if(sum<dis2[to])
			{
				dis2[to]=sum;
				bit2[to]=bi;
				q.push(make_pair(-dis2[to],to));
			}
		}
	}
}
void dfs(int x,int cnt)
{
	if(x==n)
	{
		if(judge())	ans=min(ans,cnt);
		return ;
	}
	dfs(x+1,cnt);
	vis[x]=true;
	dfs(x+1,cnt+s[x]);
	vis[x]=false;
}
void solve()
{
	
	memset(vis,false,sizeof(vis));
	vis[1]=vis[n]=true;
	dfs(2,s[1]+s[n]);
	printf("%lld",ans);
	exit(0);
}
signed main()
{
	n=read();	m=read();
	for(int i=1;i<=n;i++)
		s[i]=read();
	for(int i=1,x,y;i<=m;i++)
	{
		x=read();	y=read();
		add_edge(x,y);
	}
	memset(vis,true,sizeof(vis));
	if(!judge())
	{
		printf("-1");
		return 0;
	}
	memset(vis,false,sizeof(vis));
	Dij();
	memset(vis,false,sizeof(vis));
	Dij2();
	printf("%lld",dis2[1]);
	return 0;
}

正解

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=260,M=N*N,INF=0x3f3f3f3f3f3f3f3f;
int n,m,s[N],dis[N][N];
int tot,head[N],nxt[M],ver[M];
int tot2,head2[N],nxt2[M],ver2[M];
bool vis[N][N];
bitset<N> bit[N][N];
struct Node
{
	int dat,x,y;
	bool friend operator < (Node x,Node y)
	{
		return x.dat>y.dat;
	} 
};
priority_queue<Node> q;
void add_edge(int x,int y)
{
	ver[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
void add_edge2(int x,int y)
{
	ver2[++tot2]=y;
	nxt2[tot2]=head2[x];
	head2[x]=tot2;
}
void Dij()
{
	memset(dis,0x3f,sizeof(dis));
	dis[1][1]=s[1];	
	bit[1][1][1]=true;
	q.push((Node){dis[1][1],1,1});
	while(!q.empty())
	{
		int x=q.top().x,y=q.top().y;q.pop();
		if(vis[x][y])	continue;
		vis[x][y]=true;
		for(int i=head[x];i;i=nxt[i])
		{
			int to=ver[i],temp=0;
			if(!bit[x][y][to])	temp=s[to];
			if(dis[x][y]+temp<dis[to][y])
			{
				bit[to][y]=bit[x][y];
				bit[to][y][to]=true;
				dis[to][y]=dis[x][y]+temp;
				q.push((Node){dis[to][y],to,y});
			}
		}
		for(int i=head2[y];i;i=nxt2[i])
		{
			int to=ver2[i],temp=0;
			if(!bit[x][y][to])	temp=s[to];
			if(dis[x][y]+temp<dis[x][to])
			{
				bit[x][to]=bit[x][y];
				bit[x][to][to]=true;
				dis[x][to]=dis[x][y]+temp;
				q.push((Node){dis[x][to],x,to});
			}
		}
	}
}
signed main()
{
	n=read();	m=read();
	for(int i=1;i<=n;i++)
		s[i]=read();
	for(int i=1,x,y;i<=m;i++)
	{
		x=read();	y=read();
		add_edge(x,y);
		add_edge2(y,x);
	}
	Dij();
	printf("%lld",dis[n][n]==INF?-1ll:dis[n][n]);
	return 0;
}

T4 矩形

大坑待补

标签:NOIP,int,long,flag,35,define,pos2,8.10,pos1
来源: https://www.cnblogs.com/Varuxn/p/15134327.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有