2022杭电多校第七场题解

2022杭电多校第七场

在这里插入图片描述

Independent Feedback Vertex Set(思维)

题意
将一个无向图 G G G的顶点 V V V分成两个互补子集 V I V_I VI V F V_F VF,其中 V I V_I VI是独立集,诱导子图 G [ V F ] G[V_F] G[VF]是森林。初始状态下,图 G G G是含有三个顶点的完全图,之后会添加若干个顶点,添加的顶点会向图 G G G中存在的一条边的两个顶点各连一条边。图 G G G的每个顶点都有一个权值,求 V I V_I VI中顶点权值和的最大值。
森林:无环的无向图。
独立集:图中的一个顶点集,对于图中任意两个顶点,没有连接这两个顶点的边。

分析
图上每增加一个顶点,相当于增加了一个三元环,因此图 G G G是由若干个三元环构成。对于每一个三元环,至少要选择一个顶点作为独立集中的顶点,否则会破坏森林约束,选两个及以上的点会破坏独立集约束,于是每个三元环必须选择一个点作为独立集中的顶点。如果一个顶点确定选做独立集中的顶点,其他顶点选或不选的情况就确定了,分三种情况讨论即可。时间复杂度 O ( n ) O(n) O(n)

AC代码

typedef long long ll;
const int N=1e5+10;
struct node { int x,y; }p[N];
ll a[N],b[N];

int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n;
		cin>>n;
		for(int i=1;i<=n;i++) cin>>a[i];
		for(int i=4;i<=n;i++) cin>>p[i].x>>p[i].y;
		ll ans=0,sum;
		b[1]=b[2]=0,b[3]=1;
		sum=a[3];
		for(int i=4;i<=n;i++)
		{
			int x=p[i].x,y=p[i].y;
			if(b[x]||b[y]) b[i]=0;
			else b[i]=1,sum+=a[i];
		}
		ans=max(ans,sum);
		
		b[1]=b[3]=0,b[2]=1;
		sum=a[2];
		for(int i=4;i<=n;i++)
		{
			int x=p[i].x,y=p[i].y;
			if(b[x]||b[y]) b[i]=0;
			else b[i]=1,sum+=a[i];
		}
		ans=max(ans,sum);
		
		b[2]=b[3]=0,b[1]=1;
		sum=a[1];
		for(int i=4;i<=n;i++)
		{
			int x=p[i].x,y=p[i].y;
			if(b[x]||b[y]) b[i]=0;
			else b[i]=1,sum+=a[i];
		}
		ans=max(ans,sum);
		cout<<ans<<endl;
	}
	return 0;
}

Counting Stickmen(树形DP)

题意
给定一棵树,求树中火柴人的数量,其中火柴人的形状如下图红色结点所示。

分析
火柴人可以分为4个部分:头(2)、脖子(3)、手(4-6和9-10)、身体(5-7-8),其中只有脖子和其他三个部位都相连,因此从脖子入手,枚举可以作为脖子的位置。下面分析每个部位的特征,任何一个结点都可以作为头或者脖子,度数大于等于2的结点可以作为手,度数大于等于3的结点可以作为身体。对于一个火柴人,先确定脖子的位置,手有两个,而且两只手不能共用结点,身体只有一个,可以先确定身体的位置,之后确定手,两只手不能占用身体的位置,这就需要去重,具体可以参考下面的代码,最后确定头的位置。时间复杂度 O ( n ) O(n) O(n)

AC代码

typedef long long ll;
const int N=5e5+10;
const int mod=998244353;
vector<int> v[N];
ll d[N],body[N],hand[N];
ll ans;
int n;

ll C(ll x) { return x*(x-1)/2%mod; }

void dfs(int x,int p)
{
    ll b=0,h=0;
    ll res=0;
    for(auto y:v[x])
    {
        b=(b+body[y])%mod;
        h=(h+hand[y])%mod;
        if(y==p) continue;
        dfs(y,x);
    }
    if(d[x]<4) return ;
    for(auto y:v[x])
    {
        res=(res+body[y]*((C(h-hand[y])-(b-body[y]))%mod)%mod*(d[x]-3)%mod)%mod;
    }
    res=(res%mod+mod)%mod;
    ans=(ans+res)%mod;
}

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n;
        for(int i=1;i<=n;i++) v[i].clear();
        for(int i=1;i<=n;i++) d[i]=hand[i]=body[i]=0;
        for(int i=1;i<n;i++)
        {
            int x,y;
            cin>>x>>y;
            v[x].push_back(y);
            v[y].push_back(x);
            d[x]++,d[y]++;
        }
        for(int i=1;i<=n;i++)
        {
            hand[i]=d[i]-1;
            if(d[i]>2) body[i]=C(d[i]-1);
        }
        ans=0;
        dfs(1,0);
        cout<<ans<<endl;
    }
    return 0;
}

注意
运算过程中注意取模,中间结果可能爆long long

Black Magic(贪心)

题意
HoshiYo有 n n n个积木,每个积木的左右两边被涂成黑色或白色。HoshiYo将积木按照一定的顺序排成一排,但不旋转,然后释放黑魔法。对于任意两个相邻的积木,如果左边积木的右边和右边积木的左边都被涂成黑色,那么这两个积木就会被粘在一起,使两个积木变成一个。HoshiYo想知道释放黑魔法后他能得到的最小和最大数量的积木。

分析
对于连通块最多的情况,要让能够相连的方块对尽量少。可以发现,所有L可以全放在最左边,所有R可以全放在最右边,这样他们就不会和中间的方块相连,之后尽可能用E把B隔开即可。对于连通块最少的情况,要让能够相连的方块对尽可能多。可以先把所有B拼在一起,之后可以把一个L和一个R分为一组拼起来。注意如果有至少一组LR并且也有至少一块B的话,可以把一组LR拆开拼在B的两边,这样可以再减少一个连通块。时间复杂度 O ( 1 ) O(1) O(1)

AC代码

int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int e,l,r,b;
		cin>>e>>l>>r>>b;
		int n=e+l+r+b;
		if(b)
		{
			int ans=n;
			ans-=b-1;
			int z=max(0,min(l,r)-1);
			ans-=z;
			l-=z,r-=z;
			if(l) ans--;
			if(r) ans--;
			cout<<ans<<" ";
		}
		else
		{
			cout<<n-min(l,r)<<" ";
		}
		cout<<n-max(0,b-e-1)<<endl;
	}
	return 0;
}

Sumire(数位DP)

题意
计算 ∑ i = l r f k ( i , B , d ) \sum_{i=l}^{r}f^k(i,B,d) i=lrfk(i,B,d) f ( x , B , d ) f(x,B,d) f(x,B,d)表示数字 d d d 出现在 x x x(忽略前导0)的 B B B 进制表示下的次数。在本题中有 0 0 = 0 0^0=0 00=0.

分析
在这里插入图片描述

AC代码

const int mod=1e9+7;
typedef long long ll;
ll k,B,d,l,r;

using mint=Modint<mod>; //mint:Modint
mint dp[70][2][2][70];
bool vis[70][2][2][70];
int a[105];
int pos;

mint dfs(int cur,int limit,bool zero,int cnt)
{
	if(cur==pos&&!cnt) return 1;
	if(cur==pos) return 0;
	if(cnt<0) return 0;
	if(vis[cur][limit][zero][cnt]) return dp[cur][limit][zero][cnt];
	vis[cur][limit][zero][cnt]=1;
	dp[cur][limit][zero][cnt]=0;
	int z=limit?a[cur]:B-1;
	int used=0,v=0;
	int cost=(v==d);
	if(zero&&(d==0)) cost=0;
	dp[cur][limit][zero][cnt]+=dfs(cur+1,limit&&(a[cur]==v),zero&&(v==0),cnt-cost);
	used++;
	if(v!=d&&d<=z)
    {
        used++; v=d;
        dp[cur][limit][zero][cnt]+=dfs(cur+1,limit&&(a[cur]==v),zero&&(v==0),cnt-1);
    }
    if(v!=z)
    {
        used++; v=z;
        dp[cur][limit][zero][cnt]+=dfs(cur+1,limit&&(a[cur]==v),zero&&(v==0),cnt);
    }
    v=B;
    dp[cur][limit][zero][cnt]+=dfs(cur+1,limit&&(a[cur]==v),zero&&(v==0),cnt)*max(0,z-used+1);
    return dp[cur][limit][zero][cnt];
}

mint solve(ll n)
{
	pos=0;
	while(n) a[pos++]=n%B,n/=B;
	memset(vis,0,sizeof(vis));
	reverse(a,a+pos);
	mint ans=0;
	for(int i=1;i<=pos;i++)
	{
		ans+=dfs(0,1,1,i)*(mint(i)^k);
	}
	if(!d) return ans+1;
	return ans;
}

int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		cin>>k>>B>>d>>l>>r;
		cout<<(int)(solve(r)-solve(l-1))<<endl;
	}
	return 0;
}

Triangle Game(博弈论)

题意
Kate和Emilico正在玩一个游戏,有3个整数a,b,c,保证存在一个边长分别为a,b,c的非退化三角形。游戏过程如下:玩家轮流在这3个整数中的一个上减去某个正整数。如果在某位玩家的操作之后不存在边长为a,b,c的非退化三角形,该玩家就输了。Kate先手,如果两人都以最优策略进行游戏,Kate会赢吗?

分析
(1,1,1)是必败态,哪位玩家能到达这个状态就必胜,否则必败,这就相当于石子个数 − 1 -1 1的Nim游戏,判断 ( a − 1 ) ⊕ ( b − 1 ) ⊕ ( c − 1 ) (a-1) \oplus (b-1) \oplus (c-1) (a1)(b1)(c1)是否为 0 0 0即可。但是游戏要求每次操作后三角形是非退化的,而Nim游戏没有这个限制,下面证明这个结论仍然是正确的。

在这里插入图片描述

AC代码

int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int a,b,c;
		cin>>a>>b>>c;
		if((a-1)^(b-1)^(c-1)) cout<<"Win"<<endl;
		else cout<<"Lose"<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值