2022 1.4 补题

​​​​​​C. Klee in Solitary Confinement

思路:

首先,对于每个数我们要将其存储在数组中,注意向右位移2e6,而不是1e6,因为k的取值也可能是负数。

其次,对于每一个位置上的值是否操作,我们要进行讨论,优先将其变到x+k的位置上去,然后这个数的数目要-1,即:dp[x+k]++,dp[x]--,并且dp[x]=max(0,dp[x])保证其非负。

最后在每次要对答案取max,特判当k=0时,即不操作,此时最多的次数即为答案。

C
#include <bits/stdc++.h>
using namespace std;
#define ll long long 
#define pb push_back
const int N=6e6+5;
int a[N],dp[N],num[N];
int main(){
	int n,m=2e6,k;
	scanf("%d%d",&n,&k);
	int ans=0;
	for(int i=0;i<n;i++){
		scanf("%d",&a[i]);
		num[a[i]+m]++; 
		ans=max(ans,num[a[i]+m]);
	}if(k==0){
		cout<<ans<<"\n";
		return 0;
	}
	for(int i=0;i<n;i++){
		int x=a[i]+m;
		dp[x+k]++;
		dp[x]--;
		dp[x]=max(0,dp[x]);
		ans=max(ans,dp[x+k]+num[x+k]);
	}cout<<ans<<"\n";
}

H. Crystalfly

思路:

树形dp,定义dp状态,dp1表示以此节点为根节点所能取得的值,dp2表示以此节点为根节点但不包括其孩子结点所能取得的值。

那么进行如下讨论:

1.如果节点x的孩子结点之间没有t[y]=3的节点,那么此节点,dp2[x]=\sum_{y=0}^{y<v[x].size()} (dp1[y]-a[y])

       dp1[x]=max(dp2[x]+a[y])

2.讨论如果结点x的孩子结点中有t[y]=3的节点,那么我们可以操作,先去吃其他节点z,然后立马返回来吃为3的结点y,则结点z的孩子结点则会消失,那么节点z的贡献为dp2[z],则

dp1[x]=max(dp2[x]+a[y]+dp2[z]-(dp1[z]-a[z]))

发现与1相比,此处的变化的量为dp2[z]-dp1[z]+a[z],因此我们只需要对于每个子树都算出变化的量,然后选出最大的两个,至于为什么是最大的两个,我们可以发现,此处变化的量可能与a[y]所对应的结点是一致的,那么就会导致错误,因此我们在进行比较的时候要确定,此节点的变化的量是否为最大的,若为最大的则此节点只能加上第二大的值进行比较即可。

所以综上,我们在对每一个结点的dp1的值进行更新的时候,要分别讨论孩子结点的t[y]是否为3,优先处理出dp1[x]=max(dp2[x]+a[y]),然后当t[y]=3时,dp1[x]=max(dp1[x],dp2[x]+a[y]+maxn2)

dp1[x]=max(dp1[x],dp2[x]+a[y]+maxn1)

两个公式进行讨论后,选择其一即可。

H
#include <bits/stdc++.h>
using namespace std;
#define ll long long 
#define pb push_back
#define si size()
const int N=1e5+5;
ll a[N],dp1[N],dp2[N];
int t[N];
int n;
vector<int> v[N];
void dfs(int p,int fa){
	dp2[p]=a[p];
	ll maxn1=-1,maxn2=-1;
	for(int i=0;i<v[p].si;i++){
		int k=v[p][i];
		if(k==fa)continue;
		dfs(k,p);
		dp2[p]+=dp1[k]-a[k];
		ll tp=dp2[k]-dp1[k]+a[k]; 
		if(tp>maxn1)maxn2=maxn1,maxn1=tp;
		else if(tp>maxn2)maxn2=tp;
	}dp1[p]=dp2[p];
	for(int i=0;i<v[p].si;i++){
		int k=v[p][i];
		if(k==fa)continue;
		dp1[p]=max(dp1[p],dp2[p]+a[k]);
		if(t[k]==3){
			if(dp2[k]-dp1[k]+a[k]==maxn1)dp1[p]=max(dp1[p],dp2[p]+a[k]+maxn2);
			else dp1[p]=max(dp1[p],dp2[p]+a[k]+maxn1);
		}
	}
}
void solve(){
	cin>>n;
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]),dp1[i]=dp2[i]=0;
	for(int i=1;i<=n;i++)scanf("%d",&t[i]),v[i].clear();
	for(int i=1;i<n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		v[x].pb(y);v[y].pb(x);
	}dfs(1,0);
	cout<<dp1[1]<<"\n";
} 
int main(){
 	int t;
 	cin>>t;
 	while(t--){
 		solve();	
	}
	return 0;
}

组合数基础题

Beautiful Numbers

思路:

我们可以枚举选择a的个数x,然后可以算出所要求的的n位数的和即s=a*x+(n-x)*b;

然后我们判断s是否good数,如果s是好数,那么我们可以进行计算选x个a对应的组合数,然后加到ans里面。

注意此处计算组合数,由于题目n的范围过大1e6,所以我们不能采用递推求解C数组,因此我们此处采用求逆元的方法来求组合数,预处理出阶乘,然后和阶乘的逆元即可

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define pb push_back
const int N=2e6+5;
const int mod=1e9+7;
ll jp[N],inv[N];
int n,a,b;
ll qpow(ll x,ll k){
    ll res=1;
    while(k){
        if(k&1)res=res*x%mod;
        x=x*x%mod;
        k/=2;
    }return res;
}
void pre(){
    jp[0]=1;
    for(int i=1;i<=n;i++)jp[i]=jp[i-1]*i%mod;
    inv[0]=inv[1]=1;
    for(int i=2;i<=n;i++)inv[i]=qpow(jp[i],mod-2);
}
int check(int x){
    while(x){
        int k=x%10;
        if(k!=a&&k!=b)return 0;
        x/=10;
    }return 1;
}
int main(){
    cin>>a>>b>>n;
    ll ans=0;
    pre();
    for(int i=0;i<=n;i++){
        int num=i*a+(n-i)*b;
		if(check(num)){
		   ans=(ans+((jp[n]*inv[i])%mod*inv[n-i])%mod)%mod;
        }
    }cout<<ans<<"\n";
}

灵能传输

前缀和题目:

思路:

因为所有的操作都是在数组内部进行,因此不影响整个数组的和,而且对于每一次操作而言,改变的是连续的三个数,假设i-1,i,i+1,用s表示前缀和数组;

在一次的操作中,s[i+1]的值是不会变的,而s[i-1]的值会更新为原来的s[i],而s[i]的值会更新为原来的s[i-1]的值,

相当于经过一次操作s[i-1]和s[i]会互相交换,且我们可以进行任意次交换,那么表明除了s[0]和s[n]以外其余的s可以到达任意位置。

因此题目转化为将s数组中除了0和n的位置不变其余的位置任意交换,求出最小的(s[i]-s[i-1])的值

假设情况s[0],s[n]分别是最小的和最大的,那么我们对s数组进行排序,一次填充即可。

其他

 可能s[0]不是最小,s[n]不是最大,那么我们可以从s[0]开始隔一个取,一直取到min,然后从s[n]开始隔一个取,一直到max,然后做好取数的标记,然后在遍历一遍数组,对没有取出的数,进行加入即可。

这样能保证max(s[i]-s[i-1])是最小的。

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
const int N=3e5+5;
ll a[N],s[N];
int vis[N];
void solve(){
    int n;
    cin>>n;
    s[0]=vis[0]=0;
    for(int i=1;i<=n;i++){
        cin>>s[i];
        s[i]+=s[i-1];
        vis[i]=0;
    }
    ll s0=0,sn=s[n];
    sort(s,s+n+1);
    int l=0,r=n;
    for(int i=lower_bound(s,s+n+1,s0)-s;i>=0;i-=2){
        a[l++]=s[i];
        vis[i]=1;
    }for(int i=lower_bound(s,s+n+1,sn)-s;i<=n;i+=2){
        a[r--]=s[i];
        vis[i]=1;
    }for(int i=0;i<=n;i++){
        if(!vis[i])a[l++]=s[i];
    }ll ans=0;
    for(int i=1;i<=n;i++){
        ans=max(ans,abs(a[i]-a[i-1]));
    }cout<<ans<<"\n";
}
int main(){
    int t;
    cin>>t;
    while(t--){
        solve();
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值