7.26考试总结(NOIP模拟24)[matrix·block·graph]

你那无聊的幻想,就由我来打破!

前言

补坑中。。

我都不知道自己这场模拟赛怎么打的了。

非常玄学,前三个小时一直在想正解,然后最后 20min 感觉 T1 不太稳,就又加上了一个暴力。

后来一看只有最后码的那个 8min 码完的暴力有分,其它的就。。

T1 matrix

解题思路

考场上一眼看出是状压,然后猛想一个小时 \(n\times 2^n\) 的打法,最后骗过了样例就溜了。

正解就是状压,只不过我好像少压了一维。。

先看 70pts 的做法,枚举第 i,i-1,i+1 层的按钮状态进行转移。

\(f_{i,j,k}\) 第 i 行按钮状态为 j 上一行的按钮状态为 k。

对于同一行里的情况直接把本行的开关状态或上左移一位右移一位就好了。

实现不是很难,注意边界情况就好了,时间复杂度:\(\mathcal{O}(n\times 2^{3m})\)

考虑优化状压:对于表示的状态进行更改。

\(f_{i,j,k}\) 第 i 行覆盖状态为 j 按钮状态为 k 。

然后循环的时候枚举每一行的覆盖状态以及这一行的按钮状态还有下一行的按钮状态。

这样时候对于 i-1 行的状态就可以剪掉一些。

(证明来自 pyt )本来有 00,01,10,11四种状态,现在枚举到的实际上只有后三种,因此时间复杂度为

\(\mathcal{O}(n\times 2^{m}\times 3^{m})\)

同样是要注意边界问题。

code

70pts

#include<bits/stdc++.h>
#define int 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;
}
int n,m,INF,ans,s[15][15],val[15][15],st[15];
int f[15][1<<11][1<<11],v[15][1<<11];
string ch;
signed main()
{
	n=read();
	m=read();
	for(int i=1;i<=n;i++)
	{
		cin>>ch;
		for(int j=1;j<=m;j++)
			s[i][j]=ch[j-1]-'0';
	}
	for(int i=1;i<=n;i++)
		s[i][m+1]=s[i][0]=1;
	for(int i=1;i<=m;i++)
		s[0][i]=s[n+1][i]=1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			val[i][j]=read();
	for(int i=1;i<=n;i++)
		for(int sta=0;sta<(1<<m);sta++)
			for(int j=1;j<=m;j++)
				if((sta>>j-1)&1)
					v[i][sta]+=val[i][j];
	for(int i=1;i<=n;i++)
	{
		for(int j=m;j>=1;j--)
			st[i]=(st[i]<<1)|s[i][j];
	}
	st[0]=(1<<m)-1;
	memset(f,0x3f,sizeof(f));
	INF=f[0][0][0];
	ans=INF;
	f[1][0][0]=f[0][0][0]=0;
	for(int i=0;i<=n;i++)
		for(int s1=0;s1<(1<<m);s1++)
			for(int s2=0;s2<(1<<m);s2++)
				for(int s3=0;s3<(1<<m);s3++)
				{
					int sta=0;
					sta|=st[i]|s1|s3|s2|(s2<<1)|(s2>>1);
					sta&=(1<<m)-1;
					if(sta==(1<<m)-1&&f[i][s2][s1]<INF)
						f[i+1][s3][s2]=min(f[i+1][s3][s2],f[i][s2][s1]+v[i+1][s3]);
				}
	for(int i=0;i<(1<<m);i++)
		ans=min(ans,f[n+1][0][i]);
	printf("%lld",ans);
	return 0;
}

正解

#include<bits/stdc++.h>
#define int 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;
}
int n,m,INF,ans,s[15][15],val[15][15],st[15];
int f[15][1<<11][1<<11],v[15][1<<11];
string ch;
signed main()
{
	n=read();
	m=read();
	for(int i=1;i<=n;i++)
	{
		cin>>ch;
		for(int j=1;j<=m;j++)
			s[i][j]=ch[j-1]-'0';
	}
	for(int i=1;i<=n;i++)
		s[i][m+1]=s[i][0]=1;
	for(int i=1;i<=m;i++)
		s[0][i]=s[n+1][i]=1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			val[i][j]=read();
	for(int i=1;i<=n;i++)
		for(int sta=0;sta<(1<<m);sta++)
			for(int j=1;j<=m;j++)
				if((sta>>j-1)&1)
					v[i][sta]+=val[i][j];
	for(int i=1;i<=n;i++)
		for(int j=m;j>=1;j--)
			st[i]=(st[i]<<1)|s[i][j];
	st[0]=(1<<m)-1;
	memset(f,0x3f,sizeof(f));
	INF=f[0][0][0];
	ans=INF;
	f[1][st[1]][0]=f[0][st[0]][0]=0;
	for(int i=0;i<=n;i++)
	{
		for(int s2=0;s2<(1<<m);s2++)
		{
			for(int s3=0;s3<(1<<m);s3++)
			{
				if((s2|s3)==(1<<m)-1)				
					for(int s1=0;s1<(1<<m);s1++)
					{
						if(f[i][s2][s1]<INF)
						{
							int jt=((s3<<1)|(s3>>1)|s3|s1|st[i+1])&((1<<m)-1);
							f[i+1][jt][s3]=min(f[i+1][jt][s3],f[i][s2][s1]+v[i+1][s3]);
						}
					}
			}
		}
	}
	for(int i=0;i<(1<<m);i++)
		ans=min(ans,f[n][(1<<m)-1][i]);
	printf("%lld",ans);
	return 0;
}

T2 block

解题思路

第一问比较简单。

对于方案数,考虑将点按照 value 从大到小排序之后依次加入序列。

则对于第 i 个点放的位置需满足 \(pos-1<key_i\) ,将依次乘以位置个数即可。

注意对于 value 相等情况要特殊处理,即当 value 相等时,按照 \(key_i\)从小到大排序。

保证上一个放的位置下一个一定能放就好了。

原因显然,因为是从大到小加入的,所以最大可已放在 i 前面的数的数量一定是 key 的值和他位置的较小值。

把第二问单独拿出来搞,与第一问没有任何联系。。

一定要线段树优化,对于符合条件的数一定是选字典序较小的,这里可以在线段树里处理出来。

当我们新加入一个数的时候比它小的数之前可以放的数就会减1,直接区间修改。

同样的如果有前面只可以再放一个比他大数,那么放入的数一定是小于等于它的。

否则直接在全部区间选择字典序最小的。

边界以及细节问题非常多,尤其是某处的等号。。。

code

#include<bits/stdc++.h>
#define int long long
#define end xxxxxxxxx
#define f() cout<<"Pass"<<endl
#define ls x<<1
#define rs x<<1|1
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=5e5+10,mod=1e9+7,INF=1e18;
struct Node
{
	int val,key;
	bool friend operator < (Node x,Node y)
	{
		if(x.val==y.val)	return x.key<y.key;
		return x.val>y.val;
	}
}s[N];
struct Segment_Tree
{
	int id,kid,key,laz;
}tre[N<<2];
int n,sum,ans=1,top,sta[N],pre[N],end[N];
bool comp(Node x,Node y)
{
	if(x.val==y.val)	return x.key<y.key;
	return x.val<y.val;
}
int nmin(int x,int y)
{
	if(s[x].key==s[y].key)	return (s[x].val<s[y].val)?x:y;
	return (s[x].key<s[y].key)?x:y;
}
void push_up(int x)
{
	tre[x].key=min(tre[ls].key,tre[rs].key);
	tre[x].id=nmin(tre[ls].id,tre[rs].id);
	tre[x].kid=(tre[ls].key<=tre[rs].key)?tre[ls].kid:tre[rs].kid;
}
void push_down(int x)
{
	if(!tre[x].laz)	return ;
	tre[ls].laz+=tre[x].laz;
	tre[rs].laz+=tre[x].laz;
	tre[ls].key-=tre[x].laz;
	tre[rs].key-=tre[x].laz;
	tre[x].laz=0;
}
void build(int x,int l,int r)
{
	if(l==r)
	{
		tre[x].key=s[l].key;
		tre[x].id=l;
		tre[x].kid=l;
		return ;
	}
	int mid=(l+r)>>1;
	build(ls,l,mid);
	build(rs,mid+1,r);
	push_up(x);
}
int query(int x,int l,int r,int L,int R)
{
	if(L<=l&&r<=R)	return tre[x].id;
	push_down(x);
	int mid=(l+r)>>1,id=0;
	if(L<=mid)	id=query(ls,l,mid,L,R);
	if(R>mid)	id=nmin(id,query(rs,mid+1,r,L,R));
	push_up(x);
	return id;
}
void update(int x,int l,int r,int L,int R)
{
	if(L>R)	return ;
	if(L<=l&&r<=R)
	{
		tre[x].laz+=1;
		tre[x].key-=1;
		return ;
	}
	push_down(x);
	int mid=(l+r)>>1;
	if(L<=mid)	update(ls,l,mid,L,R);
	if(R>mid)	update(rs,mid+1,r,L,R);
	push_up(x);
}
void modify(int x,int l,int r,int pos)
{
	if(l==r)
	{
		tre[x].kid=INF;
		tre[x].key=INF;
		tre[x].id=0;
		return ;
	}
	push_down(x);
	int mid=(l+r)>>1;
	if(pos<=mid)	modify(ls,l,mid,pos);
	else	modify(rs,mid+1,r,pos);
	push_up(x);
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		s[i].key=read();
		s[i].val=read();
	}
	sort(s+1,s+n+1);
	for(int i=1;i<=n;i++)
		if(s[i].val==s[i-1].val)
		{
			ans=ans*min(i,s[i].key+sum)%mod;
			sum++;
		}
		else
		{
			ans=ans*min(i,s[i].key)%mod;
			sum=1;
		}
	printf("%lld\n",ans);
	sort(s+1,s+n+1,comp);
	s[0].key=s[0].val=INF;
	end[n]=n;
	for(int i=2;i<=n;i++)
		if(s[i].val==s[i-1].val)	pre[i]=pre[i-1];
		else	pre[i]=i-1;
	for(int i=n-1;i>=1;i--)
		if(s[i].val==s[i+1].val)	end[i]=end[i+1];
		else	end[i]=i;
	build(1,1,n);
	for(int i=1,id;i<=n;i++)
	{
		if(tre[1].key==1)	id=query(1,1,n,1,end[tre[1].kid]);
		else	id=tre[1].id;
		sta[++top]=id;
		update(1,1,n,1,pre[id]);
		modify(1,1,n,id);
	}
	for(int i=1;i<=top;i++)	printf("%lld %lld\n",s[sta[i]].key,s[sta[i]].val);
	return 0;
}

T3 graph

解题思路

尤其的恶心。。

思路就是分层图(其实就是二维 Dij 或者DPFA)+凸包维护。

首先对于不同的路径中经过的 -1 边的数量,进行分层图处理。

发现对于所有的 i 其实需要的只有 \(dis_{i,n}\)

并且对于所有的边(假设 -1 边为 x)都可以表示为 \(kx+b\)

所有相同的 k 我们只需要 b 值最小的,这里可以用凸包维护。

然后在跑一遍 Dij 就可以得出答案了。

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,M=2e3+10,Mx=1e3;
int tot,head[N],nxt[M<<1],ver[M<<1],edge[M<<1];
int n,m,top,sta[N],dis[N][N],ans[N];
bool vis[N][N];
priority_queue<pair<int,int> > 	q[N];
queue<pair<int,int> > que;
vector<int> pre[N][N];
void add_edge(int x,int y,int val)
{
	ver[++tot]=y;
	edge[tot]=val;
	nxt[tot]=head[x];
	head[x]=tot;
}
signed main()
{
	n=read();
	m=read();
	for(int i=1,x,y,val;i<=m;i++)
	{
		x=read();
		y=read();
		val=read();
		add_edge(x,y,val);
		add_edge(y,x,val);
	}
	q[0].push(make_pair(0,1));
	memset(dis,0x3f,sizeof(dis));
	dis[0][1]=0;
	for(int k=0;k<=n;k++)
		while(!q[k].empty())
		{
			int dist=-q[k].top().first,x=q[k].top().second;
			q[k].pop();
			for(int i=head[x],tmp,base,val;i;i=nxt[i])
			{
				int to=ver[i];
				if(edge[i]==-1)	tmp=1,base=1e3,val=0;
				else	tmp=0,base=0,val=edge[i];
				if(dis[k+tmp][to]>=dist+val)
				{
					if(dis[k+tmp][to]>dist+val)
					{
						vector<int>().swap(pre[k+tmp][to]);
						dis[k+tmp][to]=dist+val;
						q[k+tmp].push(make_pair(-dis[k+tmp][to],to));
					}
					pre[k+tmp][to].push_back(x+base);
				}
			}
		}
	for(int i=0;i<=n;i++)
	{
		if(top&&dis[i][n]>dis[sta[top]][n])	continue;
		while(top>1)
		{
			int tmp1=ceil((1.0*dis[sta[top]][n]-1.0*dis[i][n])/(1.0*i-1.0*sta[top]));
			int tmp2=(dis[sta[top-1]][n]-dis[i][n])/(i-sta[top-1]);
			if(tmp1>tmp2)	top--;
			else	break;
		}
		sta[++top]=i;
	}
	for(int i=1;i<=top;i++)
		que.push(make_pair(sta[i],n));
	while(!que.empty())
	{
		int x=que.front().first,y=que.front().second;
		que.pop();
		if(vis[x][y])	continue;
		vis[x][y]=ans[y]=true;
		for(int i=0;i<pre[x][y].size();i++)
			if(pre[x][y][i]>Mx)	que.push(make_pair(x-1,pre[x][y][i]-Mx));
			else	que.push(make_pair(x,pre[x][y][i]));
	}
	for(int i=1;i<=n;i++)
		printf("%lld",ans[i]);
	return 0;
}
posted @ 2021-07-28 20:59  Varuxn  阅读(75)  评论(0编辑  收藏  举报