[COCI2021-2022#1] 题解

[COCI2021-2022#1] 题解

Update on 2022.1.28

这个菜鸡已经退役了,非常遗憾这份题解未能全部完成,只能咕咕咕咕咕~

A. Ljeto

Luogu链接

记下上次击打的时间,按照题意模拟即可。

#include
using namespace std;

inline int read()
{
	int x=0,f=1;static char ch;
	while(ch=getchar(),ch<48)if(ch==45)f=0;
	do x=(x<<1)+(x<<3)+(ch^48);
	while(ch=getchar(),ch>=48);
	return f?x:-x;
}

int n,las[10],A,B;

int main()
{
	n=read();
	for(int i=1,t,a,b;i<=n;i++)
	{
		t=read(),a=read(),b=read();
		if(las[a]&&t>=las[a]+1&&t<=las[a]+10)
		{
			las[a]=t;
			if(a<=4)A+=150;
			else B+=150;
		}
		else
		{
			las[a]=t;
			if(a<=4)A+=100;
			else B+=100;
		}
	}
	printf("%d %d",A,B);
	
	return 0;
}

B. Kamenčići

Luogu链接

暴力的做法:爆搜 dfs(l,r,Ali,Bob) 表示当前剩余区间为 [ l , r ] [l,r] [l,r],Alice 和 Bob 取出的红色石头数目。每回合,轮到的人就枚举取两端的石头,若不存在一种取法使得自己必胜,对手必胜。按照这个思路可以写出:

bool dfs(int l,int r,int opt,int Ali,int Bob)// 返回1表示Alice必胜,返回0表示Bob必胜
{
	if(opt)
	{
		if(Ali+a[l]<k&&dfs(l+1,r,0,Ali+a[l],Bob))return 1;
		if(Ali+a[r]<k&&dfs(l,r-1,0,Ali+a[r],Bob))return 1;
		return 0;
	}
	else
	{
		if(Bob+a[l]<k&&!dfs(l+1,r,1,Ali,Bob+a[l]))return 0;
		if(Bob+a[r]<k&&!dfs(l,r-1,1,Ali,Bob+a[r]))return 0;
		return 1;
	}
}

容易发现有很多重复状态,于是用记忆化搜索。然而五维都设计进状态空间开不下,尝试去掉几维:

  1. 由于 Alice 和 Bob 是轮流取石子,所以剩余区间 [ l , r ] [l,r] [l,r] 对应的轮到的人是固定的,这一维可以去掉。
  2. 注意到:剩余区间为 [ l , r ] [l,r] [l,r],由于 Alice 和 Bob 之前是在 [ 1 , l − 1 ] [1,l-1] [1,l1] [ r + 1 , n ] [r+1,n] [r+1,n] 中取石子,因此他们取出的红石头总数是一定的,等于 [ 1 , l − 1 ] [1,l-1] [1,l1] [ r + 1 , n ] [r+1,n] [r+1,n] 中红石头的总数。因此 Bob 这一维可以去掉。

于是我们的状态就只剩 d p l , r , Ali dp_{l,r,\text{Ali}} dpl,r,Ali,成功解决了空间问题。最劣情况下每个状态到达一次,时间复杂度 O ( n 2 k ) O(n^2k) O(n2k)

C. Logičari

Luogu链接 loj链接

基环树 DP,简单又不简单。温馨提示:本做法处理环形 DP 时要分 24 类,请谨慎选择观看(

我们先用拓扑或其他方法找出环上的点,整颗基环树就被分为环和以环上的点为根的若干颗树。将 DP 分成旁边的若干颗树和环上进行处理,先来看看旁边的若干颗树怎么处理:

设计状态 d p x , c dp_{x,c} dpx,c 表示对于点 x x x,染色情况为 c c c 时子树中最少需要染色点数,不合法值为 i n f inf inf。染色情况分四类:

  • c = 0 c=0 c=0 x x x 不染色, x x x 的儿子都不染色。
  • c = 1 c=1 c=1 x x x 不染色, x x x 的儿子中有一个染色。
  • c = 2 c=2 c=2 x x x 染色, x x x 的儿子都不染色。
  • c = 3 c=3 c=3 x x x 染色, x x x 的儿子中有一个染色。

x x x 为叶子结点时,由于其没有儿子,故边界条件为 d p x , 0 = 0 dp_{x,0}=0 dpx,0=0 d p x , 1 = 1 dp_{x,1}=1 dpx,1=1

x x x 为非叶子结点时,四种染色情况分别有如下转移:

d p x , 0 = ∑ v ∈ s o n x d p v , 1 d p x , 1 = ∑ v ∈ s o n x d p v , 1 + min ⁡ v ∈ s o n x ( d p v , 3 − d p v , 1 ) d p x , 2 = 1 + ∑ v ∈ s o n x d p v , 0 d p x , 3 = 1 + ∑ v ∈ s o n x d p v , 0 + min ⁡ v ∈ s o n x ( d p v , 2 − d p v , 0 ) \begin{aligned} dp_{x,0}=&\sum\limits_{v\in son_x}{dp_{v,1}}\\ dp_{x,1}=&\sum\limits_{v\in son_x}{dp_{v,1}} + \min\limits_{v\in son_x}{(dp_{v,3}-dp_{v,1})}\\ dp_{x,2}=&1+\sum\limits_{v\in son_x}{dp_{v,0}}\\ dp_{x,3}=&1+\sum\limits_{v\in son_x}{dp_{v,0}} + \min\limits_{v\in son_x}{(dp_{v,2}-dp_{v,0})} \end{aligned} dpx,0=dpx,1=dpx,2=dpx,3=vsonxdpv,1vsonxdpv,1+vsonxmin(dpv,3dpv,1)1+vsonxdpv,01+vsonxdpv,0+vsonxmin(dpv,2dpv,0)
转移解释如下:

  • c = 0 c=0 c=0:对于 v v v,由于它的父亲 x x x 不染色,根据题意, v v v 的儿子必须有一个染色。又因 v v v 强制不染色,所以对应染色情况 1 1 1
  • c = 1 c=1 c=1:对于 v v v,由于它的父亲 x x x 不染色,根据题意, v v v 的儿子必须有一个染色。又因对于所有 v ∈ s o n x v\in son_x vsonx,有且仅有一个点被染色,即将其染色的贡献由 d p v , 1 dp_{v,1} dpv,1 换成 d p v , 3 dp_{v,3} dpv,3,故取更改最小的一种方式转移。
  • c = 2 c=2 c=2:对于 v v v,由于它的父亲 x x x 染色,根据题意, v v v 的儿子必须都不染色。又因 v v v 强制不染色,所以对应染色情况 0 0 0
  • c = 3 c=3 c=3:对于 v v v,由于它的父亲 x x x 染色,根据题意, v v v 的儿子必须都不染色。又因对于所有 v ∈ s o n x v\in son_x vsonx,有且仅有一个点被染色,即将其染色的贡献由 d p v , 2 dp_{v,2} dpv,2 换成 d p v , 0 dp_{v,0} dpv,0,故取更改最小的一种方式转移。

对应代码如下:

int dp[M][4];
/*
0:自己不染儿子不染 -> 父亲必染
1:自己不染儿子染   -> 父亲必不染
2:自己染  儿子不染 -> 父亲必染
3:自己染  儿子染   -> 父亲必不染 
*/
void dfs(int x,int fa)
{
	bool flag=true; 
	for(int i=hd[x];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==fa||on[v])continue;
		dfs(v,x),flag=false;
	}
	if(flag)
	{
		dp[x][0]=0,dp[x][2]=1;// 没有儿子。自己染就是 1 
		return;
	}
	dp[x][0]=0,dp[x][2]=0;
	for(int i=hd[x];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==fa||on[v])continue;
		dp[x][2]+=dp[v][0];// 儿子为0父亲必染 
		dp[x][0]+=dp[v][1];// 儿子为2父亲必染 
	}
	for(int i=hd[x];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==fa||on[v])continue;
		dp[x][3]=min(dp[x][3],dp[x][2]-dp[v][0]+dp[v][2]);// 选一个儿子染 
		dp[x][1]=min(dp[x][1],dp[x][0]-dp[v][1]+dp[v][3]);// 选一个儿子染 
	}
	dp[x][2]++,dp[x][3]++;// 最后再加,这样前面方便用 
}

然后处理环上的 DP,常用手段是断环成链。依次记形成的链上的点为 1 … m 1\dots m 1m 号点,我们枚举 1 1 1 号点的染色情况,便能推出顺理推出后续点的情况,最后得到合法的 m m m 号点的染色情况对应的染点数量。

设计状态 f i , j f_{i,j} fi,j 表示处理完环上前 i i i 个点,第 i i i 个点染色状态为 j j j 的最少染点。其中 i ≠ 1 i\ne 1 i=1 j j j 要分四类:

  • j = 0 j=0 j=0 i i i 不染色, i i i 的儿子和 i − 1 i-1 i1 都不染色。
  • j = 1 j=1 j=1 i i i 不染色, i i i 的儿子和 i − 1 i-1 i1 中有一个染色。
  • j = 2 j=2 j=2 i i i 染色, i i i 的儿子和 i − 1 i-1 i1 都不染色。
  • j = 3 j=3 j=3 i i i 染色, i i i 的儿子和 i − 1 i-1 i1 中有一个染色。

然后我们设 1 1 1 号点的染色情况为 c c c,我的垃圾写法需要分整整六类:

c c c 1 1 1 号点 1 1 1 号点的儿子 m m m 号点
0 0 0 不染 不染 不染
1 1 1 不染 有且仅有一个染 不染
2 2 2 不染 不染
3 3 3 有且仅有一个染 不染
4 4 4 不染 不染
5 5 5 不染

注:根据题意可知 1 1 1 号点的儿子和 m m m 不能同时染,因此只有六种。

对于每一类,边界条件不同。这里建议自己画图,不然真的会弄晕:

  • c = 0 c=0 c=0

    • f 2 , 0 = f 2 , 1 = inf ⁡ f_{2,0}=f_{2,1}=\inf f2,0=f2,1=inf

      解释:由于 1 1 1 号点的儿子以及 m m m 号点不染,那么 2 2 2 号点必染,因此 j j j 只能为 2 / 3 2/3 2/3

    • f 2 , 2 = d p d 1 , 0 + d p d 2 , 2 f_{2,2}=dp_{d_1,0}+dp_{d_2,2} f2,2=dpd1,0+dpd2,2

    • f 2 , 3 = d p d 1 , 0 + d p d 2 , 3 f_{2,3}=dp_{d_1,0}+dp_{d_2,3} f2,3=dpd1,0+dpd2,3

      解释:由于 1 1 1 号点及其儿子不染, 1 1 1 号点对应的 d p dp dp 染色情况为 0 0 0。由于 1 1 1 号点不染, 2 2 2 号点的 j j j 就由其对应 d p dp dp 染色情况决定。

  • c = 1 c=1 c=1

    • f 2 , 2 = f 2 , 3 = inf ⁡ f_{2,2}=f_{2,3}=\inf f2,2=f2,3=inf

      解释:由于 1 1 1 号点的儿子有且仅有一个染,那么 2 2 2 号点必不染,因此 j j j 只能为 0 / 1 0/1 0/1

    • f 2 , 0 = d p d 1 , 1 + d p d 2 , 0 f_{2,0}=dp_{d_1,1}+dp_{d_2,0} f2,0=dpd1,1+dpd2,0

    • f 2 , 1 = d p d 1 , 1 + d p d 2 , 1 f_{2,1}=dp_{d_1,1}+dp_{d_2,1} f2,1=dpd1,1+dpd2,1

      解释:由于 1 1 1 号点不染,其有个儿子染, 1 1 1 号点对应的 d p dp dp 染色情况为 1 1 1。由于 1 1 1 号点不染, 2 2 2 号点的 j j j 就由其对应 d p dp dp 染色情况决定。

  • c = 2 c=2 c=2

    • f 2 , 0 = f 2 , 1 = f 2 , 2 = inf ⁡ f_{2,0}=f_{2,1}=f_{2,2}=\inf f2,0=f2,1=f2,2=inf

      解释:由于 1 1 1 号点的儿子以及 m m m 号点不染,那么 2 2 2 号点必染。又因为 1 1 1 号点染,故 2 2 2 号点的儿子都必不染,因此 j j j 只能为 3 3 3

    • f 2 , 3 = d p d 1 , 2 + d p d 2 , 2 f_{2,3}=dp_{d_1,2}+dp_{d_2,2} f2,3=dpd1,2+dpd2,2

      解释:由于 1 1 1 号点染,儿子都不染, 1 1 1 号点对应的 d p dp dp 染色情况为 2 2 2。由于 2 2 2 号点染,儿子都不染, 2 2 2 号点对应的 d p dp dp 染色情况为 2 2 2

  • c = 3 c=3 c=3

    • f 2 , 0 = f 2 , 2 = f 2 , 3 = inf ⁡ f_{2,0}=f_{2,2}=f_{2,3}=\inf f2,0=f2,2=f2,3=inf

      解释:由于 1 1 1 号点的儿子有且仅有一个染,那么 2 2 2 号点必不染。又因为 1 1 1 号点染,故 2 2 2 号点的儿子都必不染,因此 j j j 只能为 1 1 1

    • f 2 , 1 = d p d 1 , 3 + d p d 2 , 0 f_{2,1}=dp_{d_1,3}+dp_{d_2,0} f2,1=dpd1,3+dpd2,0

      解释:由于 1 1 1 号点染,有个儿子染, 1 1 1 号点对应的 d p dp dp 染色情况为 3 3 3。由于 2 2 2 号点及其儿子都不染, 2 2 2 号点对应的 d p dp dp 染色情况为 0 0 0

  • c = 4 c=4 c=4

    • f 2 , 2 = f 2 , 3 = inf ⁡ f_{2,2}=f_{2,3}=\inf f2,2=f2,3=inf

      解释:由于 m m m 号点染,那么 2 2 2 号点必不染,因此 j j j 只能为 0 / 1 0/1 0/1

    • f 2 , 0 = d p d 1 , 0 + d p d 2 , 0 f_{2,0}=dp_{d_1,0}+dp_{d_2,0} f2,0=dpd1,0+dpd2,0

    • f 2 , 1 = d p d 1 , 0 + d p d 2 , 1 f_{2,1}=dp_{d_1,0}+dp_{d_2,1} f2,1=dpd1,0+dpd2,1

      解释:由于 1 1 1 号点不染,其有个儿子染, 1 1 1 号点对应的 d p dp dp 染色情况为 1 1 1。由于 1 1 1 号点不染, 2 2 2 号点的 j j j 就由其对应 d p dp dp 染色情况决定。

  • c = 5 c=5 c=5

    • f 2 , 0 = f 2 , 2 = f 2 , 3 = inf ⁡ f_{2,0}=f_{2,2}=f_{2,3}=\inf f2,0=f2,2=f2,3=inf

      解释:由于 m m m 号点染,那么 2 2 2 号点必不染。又因为 1 1 1 号点染,故 2 2 2 号点的儿子都必不染,因此 j j j 只能为 1 1 1

    • f 2 , 1 = d p d 1 , 2 + d p d 2 , 0 f_{2,1}=dp_{d_1,2}+dp_{d_2,0} f2,1=dpd1,2+dpd2,0

      解释:由于 1 1 1 号点染,儿子都不染, 1 1 1 号点对应的 d p dp dp 染色情况为 2 2 2。由于 2 2 2 号点及其儿子都不染, 2 2 2 号点对应的 d p dp dp 染色情况为 0 0 0

处理完边界条件后,我们开始转移:

f i , 0 = f i − 1 , 1 + d p d i , 0 f i , 1 = min ⁡ ( f i − 1 , 3 + d p d i , 0 , f i − 1 , 1 + d p d i , 1 ) f i , 2 = f i − 1 , 0 + d p d i , 2 f i , 3 = min ⁡ ( f i − 1 , 2 + d p d i , 2 , f i − 1 , 0 + d p d i , 3 ) \begin{aligned} f_{i,0}=&f_{i-1,1}+dp_{d_i,0}\\ f_{i,1}=&\min{\left(f_{i-1,3}+dp_{d_i,0},f_{i-1,1}+dp_{d_i,1}\right)}\\ f_{i,2}=&f_{i-1,0}+dp_{d_i,2}\\ f_{i,3}=&\min{\left(f_{i-1,2}+dp_{d_i,2},f_{i-1,0}+dp_{d_i,3}\right)} \end{aligned} fi,0=fi,1=fi,2=fi,3=fi1,1+dpdi,0min(fi1,3+dpdi,0,fi1,1+dpdi,1)fi1,0+dpdi,2min(fi1,2+dpdi,2,fi1,0+dpdi,3)

转移解释如下:

  • j = 0 j=0 j=0:根据定义得 i − 1 i-1 i1 不染色,又 i i i 不染色,故 i − 1 i-1 i1 的儿子和 i − 2 i-2 i2 中有一点染色,对应状态为 f i − 1 , 1 f_{i-1,1} fi1,1
  • j = 1 j=1 j=1:根据定义得 i i i 的儿子和 i − 1 i-1 i1 中有一点染色,于是分类:
    • i − 1 i-1 i1 染色:又 i i i 不染色,故 i − 1 i-1 i1 的儿子和 i − 2 i-2 i2 中有一点染色,对应状态为 f i − 1 , 3 f_{i-1,3} fi1,3
    • i i i 的儿子有一点染色:又 i i i 不染色,故 i − 1 i-1 i1 的儿子和 i − 2 i-2 i2 中有一点染色,对应状态为 f i − 1 , 1 f_{i-1,1} fi1,1
  • j = 2 j=2 j=2:根据定义得 i − 1 i-1 i1 不染色,又 i i i 染色,故 i − 1 i-1 i1 的儿子和 i − 2 i-2 i2 都不染色,对应状态为 f i − 1 , 0 f_{i-1,0} fi1,0
  • j = 3 j=3 j=3:根据定义得 i i i 的儿子和 i − 1 i-1 i1 中有一点染色,于是分类:
    • i − 1 i-1 i1 染色:又 i i i 染色,故 i − 1 i-1 i1 的儿子和 i − 2 i-2 i2 都不染色,对应状态为 f i − 1 , 2 f_{i-1,2} fi1,2
    • i i i 的儿子有一点染色:又 i i i 染色,故 i − 1 i-1 i1 的儿子和 i − 2 i-2 i2 都不染色,对应状态为 f i − 1 , 0 f_{i-1,0} fi1,0

转移完了之后,我们该统计答案了。 1 1 1 号点的每种染色情况 c c c 都对应着 m m m 号点的固定染色情况:

  • c = 0 / 1 c=0/1 c=0/1:根据定义得 m m m 不染色, 1 1 1 号点不染色,故 m m m 的儿子和 m − 1 m-1 m1 有一点染色,故对应 f m , 1 f_{m,1} fm,1
  • c = 2 / 3 c=2/3 c=2/3:根据定义得 m m m 不染色, 1 1 1 号点染色,故 m m m 的儿子和 m − 1 m-1 m1 都不染色,故对应 f m , 0 f_{m,0} fm,0
  • c = 4 c=4 c=4:根据定义得 m m m 染色, 1 1 1 号点不染色,故 m m m 的儿子和 m − 1 m-1 m1 有一点染色,故对应 f m , 3 f_{m,3} fm,3
  • c = 5 c=5 c=5:根据定义得 m m m 染色, 1 1 1 号点染色,故 m m m 的儿子和 m − 1 m-1 m1 都不染色,故对应 f m , 2 f_{m,2} fm,2

1 1 1 号点的六种情况中取需染点的最小值,若仍大于 inf ⁡ \inf inf 那么答案就为 − 1 -1 1

值得注意的是,有的状态可能没法从合法状态转移来。为了保证可读性,我们仍让 f f f 数组照常转移,但这时就可能出现 inf ⁡ \inf inf 累加的情况。因此我们不能将 inf ⁡ \inf inf 的值取得过大,定在 n + 1 n+1 n+1 是最合适的。

ans=inf;
//断环成链,枚举1号点的情况 
for(int c=0;c<=5;c++)
{
    if(c==0)
    {
        f[2][0]=f[2][1]=inf;
        f[2][2]=dp[D[1]][0]+dp[D[2]][2]; 
        f[2][3]=dp[D[1]][0]+dp[D[2]][3];
    }
    else if(c==1)
    {
        f[2][2]=f[2][3]=inf;
        f[2][0]=dp[D[1]][1]+dp[D[2]][0];
        f[2][1]=dp[D[1]][1]+dp[D[2]][1];
    }
    else if(c==2)
    {
        f[2][0]=f[2][1]=f[2][2]=inf; 
        f[2][3]=dp[D[1]][2]+dp[D[2]][2];
    }
    else if(c==3) 
    {
        f[2][0]=f[2][2]=f[2][3]=inf;
        f[2][1]=dp[D[1]][3]+dp[D[2]][0];
    }
    else if(c==4)
    {
        f[2][2]=f[2][3]=inf;

        f[2][0]=dp[D[1]][0]+dp[D[2]][0];
        f[2][1]=dp[D[1]][0]+dp[D[2]][1];
    }
    else
    {
        f[2][0]=f[2][2]=f[2][3]=inf;
        f[2][1]=dp[D[1]][2]+dp[D[2]][0];
    }

    for(int i=3;i<=cnt;i++)
    {
        f[i][0]=f[i-1][1]+dp[D[i]][0];
        f[i][1]=min(f[i-1][3]+dp[D[i]][0],f[i-1][1]+dp[D[i]][1]);
        f[i][2]=f[i-1][0]+dp[D[i]][2];
        f[i][3]=min(f[i-1][2]+dp[D[i]][2],f[i-1][0]+dp[D[i]][3]);
    }
    if(c==0)ans=min(ans,f[cnt][1]);
    if(c==1)ans=min(ans,f[cnt][1]);
    if(c==2)ans=min(ans,f[cnt][0]);
    if(c==3)ans=min(ans,f[cnt][0]);
    if(c==4)ans=min(ans,f[cnt][3]);
    if(c==5)ans=min(ans,f[cnt][2]);
}

D. Set

Luogu链接 loj链接

E. Volontiranje

Luogu链接 loj链接

你可能感兴趣的:(题解,c++,算法)