6.23 NOIp2019 第四次模拟赛 订正总结

知识共享许可协议 版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons

Directory is in hand, the world I have

Thinking process

  • 2:30拿题,开头映入眼帘的是
    在这里插入图片描述
    额,要凉凉啊。
  • 前半个小时,看题,写方程,搞算法。
    • 先去搞了 T 2 T2 ,毕竟写过,就照着思路写了,最后样例一直输出 68,很无语,后来发现是状态转移方程打错了,赶紧改过来,此时 3:20,看到 C h d y Chdy 大佬已经写完,并且上去评测了,结果 A K AK !收下小弟的膝盖。
    • 后过头来搞 T 1 T1 ,很明显是一道树形Dp,然而太长时间没写,忘了套路了,磕磕绊绊,后被 T y o u c h i e Tyouchie 提醒“你枚举两种情况”,猛的想到可以分这个节点选还是不选,然后判断合法,ok,有了思路,然后想着 d f s dfs 一下,加上数据范围,很明显 O ( N ) O(N) 算法,就过了。
    • T 3 T3 实在不会啊,设状态设成 f [ s ] f[s] 表示状态为 s s 的方案数,结果不会转移,也没想出他有后效性,就 gg 了,然后此时还有半个小时比赛结束,于是乎不想写了,直接交上去,评测 200 p t s 200 pts ,看到三位强者全是 300 p t s 300 pts 的我瞬间感觉, 200 p t s 200 pts 可能要被踩爆了,要垫底了,算了,交过了,就不说这事了,去问了 T y o u c h i e Tyouchie T 3 T3 的写法,然后给 T y o u c h i e Tyouchie 口胡一段二分图,额,比赛就结束了。
  • 成绩? r a n k 5 rank 5 吧,觉得大家没发挥好。 J . J. 问这次题这么简单,下次要不要难些啊,我。。(冒汗)。
  • 最后吐槽一下出题人取名字真随便。

a

title

在这里插入图片描述
在这里插入图片描述

analysis

在这里插入图片描述

写完之后,就可以说:这就是一道树形Dp入门题了。

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;

char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1, ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}

template<typename T>inline void write(T x)
{
	if (!x) { putchar('0'); return ; }
	if (x<0) putchar('-'), x=-x;
	T num=0, ch[20];
	while (x) ch[++num]=x%10+48, x/=10;
	while (num) putchar(ch[num--]);
}

int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y)
{
	ver[++len]=y,Next[len]=head[x],head[x]=len;
}

int f[maxn][2];
inline void dfs(int x,int fa)
{
	f[x][1]=1,f[x][0]=0;
	for (int i=head[x]; i; i=Next[i])
	{
		int y=ver[i];
		if (y==fa) continue;
		dfs(y,x);
		f[x][0]+=max(f[y][1],f[y][0]);
		f[x][1]+=f[y][0];
	}
}

int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	int n;
	read(n);
	for (int i=1,x,y; i<=n-1; ++i) read(x),read(y),add(x,y),add(y,x);
	dfs(1,0);
	write(max(f[1][1],f[1][0]));
	return 0;
}

b

title

在这里插入图片描述
在这里插入图片描述

analysis

在这里插入图片描述
某谷上好像有这道题,环形石子合并
如果写过的话,务必 A A 了这道题来压惊!

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;

char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1, ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}

template<typename T>inline void write(T x)
{
	if (!x) { putchar('0'); return ; }
	if (x<0) putchar('-'), x=-x;
	T num=0, ch[20];
	while (x) ch[++num]=x%10+48, x/=10;
	while (num) putchar(ch[num--]);
}

int v[maxn],sum[maxn],f[maxn][maxn];
int main()
{
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	int n;
	read(n);
	for (int i=1; i<=n; ++i) read(v[i]),v[n+i]=v[i];
	for (int i=1; i<=(n<<1); ++i) sum[i]=sum[i-1]+v[i];//处理前缀和
	for (int len=2; len<=n; ++len)
		for (int l=1; l+len-1<=(n<<1); ++l)
		{
			int r=l+len-1;
			for (int k=l; k+1<=r; ++k) f[l][r]=max(f[l][r],f[l][k]+f[k+1][r]);//状态转移,模拟合并
			f[l][r]+=sum[r]-sum[l-1];//最后加上这个区间的能量
		}
	int ans=0;
	for (int i=1; i<=n; ++i) ans=max(ans,f[i][i+n-1]);
	write(ans);
	return 0;
}

c

title

在这里插入图片描述
在这里插入图片描述

analysis

在这里插入图片描述
在这里插入图片描述
考场上被告知正解为状压Dp,然而设状态没搞成,就 gg 了。后来被 C h d y Chdy 大佬严格证明一维状态有后效性,可能会将错误状态强行转成正确转态来统计,是错误的。嗯,又在大佬的帮助下,A 掉此题。

code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxs=1<<16,maxn=17,maxh=25e3+10;

char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1, ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}

template<typename T>inline void write(T x)
{
	if (!x) { putchar('0'); return ; }
	if (x<0) putchar('-'), x=-x;
	T num=0, ch[20];
	while (x) ch[++num]=x%10+48, x/=10;
	while (num) putchar(ch[num--]);
}

ll f[maxs][maxn],h[maxh];
int main()
{
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	int n,k;read(n);read(k);
	for (int i=1; i<=n; ++i) read(h[i]);
	for (int i=1; i<=n; ++i) f[1<<i-1][i]=1;//在第i位放上i的方案数为1
	for (int s=1; s<=(1<<n)-1; ++s)//枚举所有可能的状态
		for (int i=1; i<=n; ++i)//枚举前一个填进去的数字
			for (int j=1; j<=n; ++j)//枚举现在要填进去的数字
			{
				if (s&(1<<j-1)) continue;//如果他已被填进去
				if (abs(h[i]-h[j])>k) f[s|(1<<j-1)][j]+=f[s][i];//由前一个状态,填进前一个数字转移过来
			}
	ll ans=0;
	for (int i=1; i<=n; ++i) ans+=f[(1<<n)-1][i];
	write(ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/huashuimu2003/article/details/93402241
今日推荐