NOIP模拟70

T1 暴雨

解题思路

\(f_{i,j,k,0/1}\) 表示前 i 个铲平 j 个当前最高的是 k 并且当前是 奇数/偶数 的方案数。

由于只可以铲平 k 块,因此对于同一种 \(i,j\) 而言高度只可能有 k+1 种,这些我们可以用 set 预处理出来。。

然后前后都做一遍 DP 在合并的时候枚举最高高度,同时枚举左右的最大值取值计入答案就好了。

code

#include <bits/stdc++.h>
#define ull unsigned long long
#define f() cout<<"Failed"<<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=25e3+10,mod=1e9+7;
int n,m,num[N];long long ans;
struct Node
{
	int s[N],f[N][26][30][2],rec[N][30],all[N];
	unordered_map<int,int> mp[N];
	set<int> se;
	void solve()
	{
		for(int i=0;i<=n;i++)
		{
			for(auto it=se.begin();it!=se.end();it++)
				mp[i].insert(make_pair((*it),++all[i])),rec[i][all[i]]=(*it);
			if(mp[i].find(s[i])==mp[i].end()) mp[i].insert(make_pair(s[i],++all[i])),rec[i][all[i]]=s[i];
			se.insert(s[i]); if(se.size()>m+1) se.erase(se.begin());
		}
		f[0][0][1][0]=1;
		for(int i=0;i<n;i++)
			for(int j=0;j<=m;j++)
				for(int k=1;k<=all[i];k++)
					for(int pos=0;pos<=1;pos++)
						if(f[i][j][k][pos])
						{
							int val=max(s[i+1],rec[i][k]),p=pos^(val-s[i+1])&1;
							int id=mp[i+1].find(val)->second;
							f[i+1][j][id][p]=(f[i+1][j][id][p]+f[i][j][k][pos])%mod;
							if(j>=m) continue;
							val=rec[i][k]; id=mp[i+1].find(val)->second; p=pos^(val&1);
							f[i+1][j+1][id][p]=1ll*(1ll*f[i+1][j+1][id][p]+1ll*f[i][j][k][pos])%mod;
						}
	}
}pre,suf;
signed main()
{
	freopen("rain.in","r",stdin); freopen("rain.out","w",stdout);
	n=read(); m=read();
	for(int i=1;i<=n;i++) pre.s[i]=num[i]=read(),suf.s[n-i+1]=num[i];
	pre.solve(); suf.solve();
	for(int i=1;i<=n;i++)
		for(int j=0;j<=m;j++)
			for(int k=1;k<=pre.all[i];k++)
				if(pre.rec[i-1][k]<num[i])
					for(int pos=0;pos<=1;pos++)
						if(pre.f[i-1][j][k][pos])
							for(int q=1;q<=suf.all[n-i];q++)
								if(suf.rec[n-i][q]<=num[i])
									ans=(ans+1ll*pre.f[i-1][j][k][pos]*suf.f[n-i][m-j][q][pos])%mod;
	printf("%lld",ans);
	return 0;
}

T2 AVL 树

解题思路

首先对于整棵树我们可以初始化出来对于不同深度的树至少有多少节点。

然后对于子树进行搜索优先搜左子树,每次跳父亲看是否可以加入,由于这是一颗 AVL 树,因此树高接近 \(log\)

于是我们就可以对于父亲节点来更新自己的限制,并且根据左子树来更新右子树的限制。

code

#include <bits/stdc++.h>
#define int long long 
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
#define ls son[x][0]
#define rs son[x][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;
int n,m,root,mxdep[N],dep[N],use[N],lim[N],g[50],fa[N],son[N][2];
bool vis[N];
void dfs(int x)
{
	dep[x]=dep[fa[x]]+1; mxdep[x]=dep[x];
	if(ls) dfs(ls),mxdep[x]=max(mxdep[x],mxdep[ls]);
	if(rs) dfs(rs),mxdep[x]=max(mxdep[x],mxdep[rs]);
}
bool judge(int x)
{
	int temp=max(dep[x],use[x]),cnt=0;
	while(x)
	{
		cnt+=vis[x]^1; temp=max(temp,use[x]);
		if(x==son[fa[x]][0]&&son[fa[x]][1])
			cnt+=g[max(temp-1,lim[son[fa[x]][1]])-dep[fa[x]]];
		x=fa[x];
	}
	return cnt<=m;
}
void insert(int x)
{
	int temp=use[x]=max(use[x],dep[x]);
	while(x)
	{
		m-=vis[x]^1; vis[x]=true; use[x]=max(use[x],temp);
		if(x==son[fa[x]][0]&&son[fa[x]][1]&&!vis[son[fa[x]][1]])
			lim[son[fa[x]][1]]=max(lim[son[fa[x]][1]],use[x]-1);
		x=fa[x];
	}
}
void work(int x)
{
	int tmp1=ls,tmp2=rs; if(mxdep[ls]>=lim[x]) swap(tmp1,tmp2);
	lim[tmp1]=max(lim[tmp1],lim[x]-1); lim[tmp2]=max(lim[tmp2],lim[x]);
}
void solve(int x)
{
	if(judge(x)) insert(x);
	if(ls&&rs) work(x);
	else if(ls) lim[ls]=max(lim[ls],lim[x]);
	else if(rs) lim[rs]=max(lim[rs],lim[x]);
	if(ls) solve(ls); if(rs) solve(rs);
}
signed main()
{
	freopen("avl.in","r",stdin); freopen("avl.out","w",stdout);
	n=read(); m=read();
	g[1]=1; for(int i=2;i<=40;i++) g[i]=g[i-1]+g[i-2]+1;
	for(int i=1;i<=n;i++)
	{
		fa[i]=read();
		if(~fa[i]&&fa[i]<i) son[fa[i]][1]=i;
		else if(~fa[i]&&fa[i]>i) son[fa[i]][0]=i;
		else root=i;
	}
	fa[root]=0; dfs(root); solve(root);
	for(int i=1;i<=n;i++) printf("%lld",(int)vis[i]);
	return 0;
}

T3 挖掘机

解题思路

比较暴力的一个思路就是对于每一个点求出它只操作一次可以到达的最远位置,然后依次跳链表。

这个东西是可以倍增优化的,每次跳 \(2^i\) 步,代码实现也不太难。。

只可惜我一直以为是线段树维护。。

code

#include <bits/stdc++.h>
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;
int n,m,q,len,nxt[13][N],f[13][N][18];
char s[13][N];
void solve()
{
	int dep,l,r,ans=0; dep=read(); l=read(); r=read();
	for(int i=1;i<=dep;i++)
	{
		int pos=l;
		if(s[i][pos]=='.') pos=nxt[i][pos]+1;
		for(int j=17;j>=0&&pos<=r;j--)
			if(f[i][pos][j]&&f[i][pos][j]<=r)
				ans+=1ll<<j,pos=f[i][pos][j];
		if(s[i][pos]=='.') pos=nxt[i][pos]+1;
		if(pos<=r) ans++;
	}
	printf("%d\n",ans);
}
void Init_Work(int pos)
{
	for(int i=m;i>=1;i--)
	{
		if(s[pos][i]=='X') f[pos][i][0]=i+len;
		else f[pos][i][0]=nxt[pos][i]+len+1;
		for(int j=0;f[pos][i][j];j++)
			f[pos][i][j+1]=f[pos][f[pos][i][j]][j];
	}
}
signed main()
{
	freopen("blueshit.in","r",stdin); freopen("blueshit.out","w",stdout);
	n=read(); m=read(); len=read(); q=read();
	for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
	for(int i=1;i<=n;i++)
		for(int j=m,las;j>=1;j--)
		{
			if(s[i][j]!=s[i][j+1]) las=j;
			nxt[i][j]=las;
		}
	for(int i=1;i<=n;i++) Init_Work(i);
	while(q--) solve();
	return 0;
}

T4 游戏

大坑未补

posted @ 2021-10-08 09:26  Varuxn  阅读(101)  评论(0编辑  收藏  举报