8.14考试总结(NOIP模拟40)[打地鼠·竞赛图·糖果·树]

T1 打地鼠

解题思路

数据范围比较小,不需要什么优化。

直接二维前缀和枚举右下角端点就好了。

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=2e3+10;
int n,m,ans,s[N][N],pre[N][N];
char ch[N];
signed main()
{
	n=read();	m=read();
	for(int i=1;i<=n;i++)
	{
		scanf("%s",ch+1);
		for(int j=1;j<=n;j++)
			s[i][j]=ch[j]-'0';
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+s[i][j];
	if(m>=n)
	{
		printf("%lld",pre[n][n]);
		return 0;
	}
	for(int i=m;i<=n;i++)
		for(int j=m;j<=n;j++)
			ans=max(ans,pre[i][j]-pre[i-m][j]-pre[i][j-m]+pre[i-m][j-m]);
	printf("%lld",ans);
	return 0;
}

T2 竞赛图

解题思路

设 \(e(S)\) 表示点集 \(S\) 向点集以外的点的连边的交集为 \(e(S)\) 。

我们已经知道了每一个点向外面连边的点集了。

因此可以把大的集合拆成两部分对于两部分的 e 取交集。

这里为了方便直接取了 \(lowbit\) 。。。

因为保证了每两个点之间只有一条有向边,因此 \(S\) 向 \(e(S)\) 的任何一个子集的连边一定是单向的。

因此他们共同构成的一个点集不是强联通的,其他的就是合法的了。

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=25;
int T,n,ans,e[1<<N];
bool jud[1<<N];
int lowbit(int x){return x&(-x);}
void solve()
{
	memset(e,0,sizeof(e));
	memset(jud,true,sizeof(jud));
	n=read();	ans=0;
	e[0]=(1<<n)-1;
	for(int i=1;i<=n;i++)
		for(int j=1,x;j<=n;j++)
		{
			x=read();
			e[1<<i-1]|=x<<j-1;
		}
	for(int i=1;i<(1<<n);i++)
		e[i]=e[i^lowbit(i)]&e[lowbit(i)];
	for(int i=1;i<(1<<n);i++)
		if(jud[i])
			for(int j=e[i];j;j=(j-1)&e[i])
				jud[i|j]=false;
	for(int i=0;i<(1<<n);i++)
		ans+=jud[i];
	printf("%lld\n",ans);
}
signed main()
{
	T=read();
	while(T--)	solve();
	return 0;
}

T3 糖果

解题思路

动态规划

设 \(f_{i,j,k}\) 表示当前小A选择到 i ,小B选择到 j ,小C还有 k 个备选位置。

如果 i 当前扫到的数在 a 中的优先级比 b 小,那么就一定是被小C 拿走了,先加到以后的里面去。

否则的话就相当于是小A选走了,那么此时的小C的备选位置就少了一个。

然后对于小B 的选择也是类似。

为了方便转移,我们再给 f 数组加一维\(0/1\) 表示当前进行到了那一步。

由于我太菜了,至今都没想到问什么后面要乘上一个 \(3\times i-1\)和 \(3\times i -2\)。(逃

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=410,M=150,mod=1e9+7;
int n,ans,f[N][N][M][2],a[N],b[N],pos1[N],pos2[N];
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		pos1[a[i]]=i;
	}
	for(int i=1;i<=n;i++)
	{
		b[i]=read();
		pos2[b[i]]=i;
	}
	f[1][1][0][0]=1;
	for(int i=1;i<=n+1;i++)
		for(int j=1;j<=n+1;j++)
			for(int k=0;k<=n/3;k++)
			{
				if(f[i][j][k][0])
				{
					if(i==n+1)
					{
						if(!k)	ans=(ans+f[i][j][k][0])%mod;
						continue;
					}
					if(pos2[a[i]]<j)	(f[i+1][j][k][0]+=f[i][j][k][0])%=mod;
					else
					{
						(f[i][j][k][1]+=f[i][j][k][0])%=mod;
						if(k)	(f[i+1][j][k-1][0]+=f[i][j][k][0]*k%mod)%=mod;
					}
				}
				if(f[i][j][k][1])
				{
					if(pos1[b[j]]<i)	(f[i][j+1][k][1]+=f[i][j][k][1])%=mod;
					else	if(a[i]!=b[j])
					{
						(f[i+1][j+1][k+1][0]+=f[i][j][k][1])%=mod;
						if(k)	(f[i][j+1][k-1][1]+=f[i][j][k][1]*k%mod)%=mod;
					}
				}
			}
	for(int i=1;i<=n;i++)
		if(i%3)
			ans=ans*i%mod;
	printf("%lld",ans);
	return 0;
}

T4 树

解题思路

几乎是今年 NOI 的原题吧(尽管我没有资格去考,也没有看过这个题)

在每一次操作的时候给每一个点涂上一种新的颜色。

那么两端颜色相同的就是白色边,反之就是黑色边。

然后就可以直接树链剖分+线段树维护当前区间的颜色种类数以及两端的颜色了。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#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=3e5+10;
int n,q,col;
int tot,head[N],ver[N<<1],nxt[N<<1];
int tim,dep[N],siz[N],son[N],topp[N],fa[N],dfn[N],id[N];
struct Segment_Tree
{
	int dat,laz,lc,rc;
}tre[N<<2];
void add_edge(int x,int y)
{
	ver[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
void dfs1(int x)
{
	siz[x]=1;
	for(int i=head[x];i;i=nxt[i])
	{
		int to=ver[i];
		if(siz[to])	continue;
		dep[to]=dep[x]+1;
		fa[to]=x;
		dfs1(to);
		siz[x]+=siz[to];
		if(siz[to]>siz[son[x]])
			son[x]=to;
	}
}
void dfs2(int x,int tp)
{
	dfn[x]=++tim;
	id[tim]=x;
	topp[x]=tp;
	if(son[x])	dfs2(son[x],tp);
	for(int i=head[x];i;i=nxt[i])
		if(!dfn[ver[i]])
			dfs2(ver[i],ver[i]);
}
void push_up(int x)
{
	tre[x].dat=tre[ls].dat+tre[rs].dat+(tre[ls].rc!=tre[rs].lc);
	tre[x].lc=tre[ls].lc;	tre[x].rc=tre[rs].rc;
}
void push_down(int x)
{
	if(!tre[x].laz)	return ;
	tre[ls].dat=tre[rs].dat=0;
	tre[ls].lc=tre[rs].lc=tre[ls].rc=tre[rs].rc=tre[x].laz;
	tre[ls].laz=tre[rs].laz=tre[x].laz;
	tre[x].laz=0;
}
void build(int x,int l,int r)
{
	if(l==r)
	{
		tre[x].lc=tre[x].rc=++col;
		tre[x].dat=0;
		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 pos)
{
	if(l==r)	return tre[x].lc;
	push_down(x);
	int mid=(l+r)>>1,ans;
	if(pos<=mid)	ans=query(ls,l,mid,pos);
	else	ans=query(rs,mid+1,r,pos);
	push_up(x);
	return ans;
}
int query(int x,int l,int r,int L,int R)
{
	if(L<=l&&r<=R)	return tre[x].dat;
	push_down(x);
	int mid=(l+r)>>1,sum=0;
	if(L<=mid)	sum+=query(ls,l,mid,L,R);
	if(R>mid)	sum+=query(rs,mid+1,r,L,R);
	sum+=(L<=mid&&R>mid&&tre[ls].rc!=tre[rs].lc);
	push_up(x);
	return sum;
}
void update(int x,int l,int r,int L,int R,int num)
{
	if(L<=l&&r<=R)
	{
		tre[x].dat=0;
		tre[x].lc=tre[x].rc=num;
		tre[x].laz=num;
		return ;
	}
	push_down(x);
	int mid=(l+r)>>1;
	if(L<=mid)	update(ls,l,mid,L,R,num);
	if(R>mid)	update(rs,mid+1,r,L,R,num);
	push_up(x);
}
void update(int x,int y)
{
	col++;
	while(topp[x]^topp[y])
	{
		if(dep[topp[x]]<dep[topp[y]])
			swap(x,y);
		update(1,1,tim,dfn[topp[x]],dfn[x],col);
		x=fa[topp[x]];
	}
	if(dep[x]>dep[y])	swap(x,y);
	update(1,1,tim,dfn[x],dfn[y],col);
}
int solve(int x,int y)
{
	int sum=0;
	while(topp[x]^topp[y])
	{
		if(dep[topp[x]]<dep[topp[y]])
			swap(x,y);
		sum+=query(1,1,tim,dfn[topp[x]],dfn[x]);
		sum+=(query(1,1,tim,dfn[topp[x]])!=query(1,1,tim,dfn[fa[topp[x]]]));
		x=fa[topp[x]];
	}
	if(dep[x]>dep[y])	swap(x,y);
	sum+=query(1,1,tim,dfn[x],dfn[y]);
	return sum;
}
signed main()
{
	n=read();
	for(int i=1,x,y;i<n;i++)
	{
		x=read();	y=read();
		add_edge(x,y);
		add_edge(y,x);
	}
	dfs1(1);
	dfs2(1,1);
	build(1,1,tim);
	q=read();
	while(q--)
	{
		int opt,x,y;
		opt=read();	x=read();	y=read();
		if(opt==1)	update(x,y);
		else	if(x!=y)	printf("%lld\n",solve(x,y));
		else	printf("0\n");
	}
	return 0;
}
上一篇:BZOJ4212 神牛的养成计划 (字典树,bitset)


下一篇:主席树的一些理解