2022 年第四届河南省 CCPC 大学生程序设计竞赛vp补题

Dashboard - 2022 CCPC Henan Provincial Collegiate Programming Contest - Codeforces

Problem B. Hash

思路:

  1. 发现31的次幂取模的答案,所以如果一段太长肯定不如拆成2段。
  2. 首先如果一段长度为7,那么无论他的开头是a,eh,n的谁,都有val>=31^6=887503681>P/2
  3. 所以我一定可以把长度大于等于14的拆成2段,使得val(7)+val(7)>P>val(14)
  4. 明确每一段长度才这么一点,我们可以暴力枚举dp,求dp[i],我们每次求出以i结尾的各种长度len(1~13)的val,那么max(dp[i])=max(dp[i-len]+val(len))。因为是环,所以我们需要枚举第一段的可能的13个开头(一段最长为13)
#include <bits/stdc++.h>
using namespace std;
#define ll               long long
#define endl             "\n"
const int mod=998244353;
const int N=2e5+50;
ll a[N],d[30],dp[N],pre[20];
void mysolve()
{
	string s;
	cin>>s;
	int n=s.size();
	for(int i=0; i<n; ++i)
		{
			if(s[i]=='a')a[i]=1;
			else if(s[i]=='e')a[i]=2;
			else if(s[i]=='h')a[i]=3;
			else a[i]=4;
		}
	ll ans=0;
	pre[0]=1;
	for(int i=1; i<=13; ++i)pre[i]=pre[i-1]*31%mod;
	for(int k=0; k<13; ++k)//枚举开头
		{
			vector<ll>dp(n+15);
			int l=k,r=n+l-1;
			a[r+1]=a[l];//因为开头移动,所以尾部也移动了
			for(int i=l; i<=r; ++i)//遍历dp[i]
				{
					ll tmp=0;
					for(int j=0; j<14&&i-j>=l; ++j)//枚举以i结尾的最优长度获取dp[i]
						{
							tmp=(tmp+a[i-j]*pre[j])%mod;
							if(i-j)dp[i]=max(dp[i],dp[i-j-1]+tmp);
						}
				}
			ans=max(ans,dp[r]);
		}
	cout<<ans<<endl;
}

int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	mysolve();
	return 0;
}

Problem C. Serval 的试卷答案

思路:

  1. 对于s[i]>s[i+1]的,显然我们必须把他们隔开。
  2. 而我们需要获取k段,显然需要插k-1个隔板,如果我们把必须隔开的隔开后,剩下可以插空的位置就是可以容易插。所以对于一段[l,r]划分k段,要求必须划分的有cnt段。答案就是组合数\binom{r-l-cnt}{k-1-cnt},显然我们线段树维护好cnt即可
#include <bits/stdc++.h>
using namespace std;
#define ll               long long
#define endl             "\n"
const int N = 1e5 + 20;
const int mod=998244353;
int a[N];
int n,q;
#define ls p<<1
#define rs p<<1|1
#define mid (t[p].l+((t[p].r-t[p].l)>>1))
ll pre[N],dev[N],tmp[4][4];
struct tree
{
	int l,r;
	int ld,rd;
	int add,cnt;
	int sum[4][4];
} t[N<<2];

inline ll fastmi(ll base,ll power)
{
	ll ans=1;
	while(power)
		{
			if(power&1)ans=ans*base%mod;
			base=base*base%mod;
			power>>=1;
		}
	return ans;
}

inline void pushup(int p)
{
	for(int i=0; i<4; ++i)for(int j=0; j<4; ++j)t[p].sum[i][j]=t[ls].sum[i][j]+t[rs].sum[i][j];
	t[p].sum[t[ls].rd][t[rs].ld]++;
	t[p].ld=t[ls].ld,t[p].rd=t[rs].rd;
	t[p].cnt=t[ls].cnt+t[rs].cnt;
	if(t[ls].rd>=t[rs].ld)t[p].cnt++;
}

void build(int l,int r,int p)
{
	t[p].l=l,t[p].r=r;
	if(t[p].l==t[p].r)
		{
			t[p].ld=t[p].rd=a[l];
			return;
		}
	build(l,mid,ls),build(mid+1,r,rs);
	pushup(p);
}

inline void change(int p,int w)
{
	for(int i=0; i<4; ++i)for(int j=0; j<4; ++j)tmp[i][j]=t[p].sum[i][j];
	for(int i=0; i<4; ++i)for(int j=0; j<4; ++j)t[p].sum[(i+w)%4][(j+w)%4]=tmp[i][j];
	t[p].ld=(t[p].ld+w)%4,t[p].rd=(t[p].rd+w)%4;
	t[p].cnt=0;
	for(int i=0; i<4; ++i)for(int j=i; ~j; --j)t[p].cnt+=t[p].sum[i][j];
	t[p].add+=w;
}

inline void lazy(int p)
{
	if(t[p].l==t[p].r)t[p].add=0;
	if(t[p].add)
		{
			change(ls,t[p].add),change(rs,t[p].add);
			t[p].add=0;
		}
}

void update(int l,int r,int p)
{
	if(l<=t[p].l&&t[p].r<=r)
		{
			change(p,1);
			return;
		}
	lazy(p);
	if(mid>=l)update(l,r,ls);
	if(mid<r)update(l,r,rs);
	pushup(p);
}

int ask(int l,int r,int p)
{
	if(l<=t[p].l&&t[p].r<=r)return t[p].cnt;
	int ans=0;
	lazy(p);
	if(mid>=l)ans+=ask(l,r,ls);
	if(mid<r)ans+=ask(l,r,rs);
	if(l<=mid&&mid<r)ans+=(t[ls].rd>=t[rs].ld);
	return ans;
}

inline ll C(int n,int m)
{
	if(m>n||m<0||n<0)return 0;
	return 1ll*pre[n]*dev[m]%mod*dev[n-m]%mod;
}

void mysolve()
{
	cin>>n>>q;
	string s;
	cin>>s;
	for(int i=0; i<n; ++i)a[i+1]=s[i]-'A';
	build(1,n,1);
	int l,r,k,op;
	while(q--)
		{
			cin>>op>>l>>r;
			if(op==1)update(l,r,1);
			else
				{
					cin>>k;
					int cnt=ask(l,r,1);
					cout<<C(r-l-cnt,k-1-cnt)<<endl;
				}
		}
}

int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	pre[0]=1;
	int nx=1e5;
	for(int i=1; i<=nx; ++i)pre[i]=pre[i-1]*i%mod;
	dev[nx]=fastmi(pre[nx],mod-2)%mod;
	for(int i=nx-1; ~i; --i)dev[i]=dev[i+1]*(i+1)%mod;
	mysolve();
	system("pause");
	return 0;
}

Problem J. Mex Tree

思路:

  1. 如果mex=n,显然答案为n
  2. 我们以val[i]=0的i设为根rt
  3. 显然mex=0时答案就是根节点的最大子树
  4. 其他情况,mex=k,显然val[i]=k的点不能包含,又必须包含小于k的所有点,显然我们答案就是取除k的子树之外的所有点(因为0点为根节点,k子树内的点与0相连必须经过k),如果k子树内存在点小于k,那么答案为0,如果都大于k,那么答案为n-sz[k](k子树大小)
  5. 因此,我们需要维护每个点的子树的包含的最小值
#include <bits/stdc++.h>
using namespace std;
#define ll               long long
#define endl             "\n"
#define int              long long
typedef pair<int, int> pii;
//---------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------//
//double 型memset最大127,最小128
const int INF = 0x3f3f3f3f;         //int型的INF
const ll llINF = 0x3f3f3f3f3f3f3f3f;//ll型的llINF
const double eps=1e-9;
const int N = 1e6 + 10;
int a[N],sz[N],mn[N],mx[N],b[N];
vector<int>edge[N];
void dfs(int u,int f)
{
	sz[u]=1,mn[u]=a[u];
	mx[u]=0;
	for(auto v:edge[u])if(v!=f)
			{
				dfs(v,u);
				mx[u]=max(mx[u],sz[v]);
				sz[u]+=sz[v];
				mn[u]=min(mn[u],mn[v]);
			}
}
void mysolve()
{
	int n;
	cin>>n;
	int rt;
	for(int i=1; i<=n; ++i)
		{
			cin>>a[i];
			b[a[i]]=i;
			if(a[i]==0)rt=i;
		}
	int x;
	for(int i=2; i<=n; ++i)cin>>x,edge[x].push_back(i),edge[i].push_back(x);
	dfs(rt,-1);
	cout<<mx[rt]<<" ";
	for(int i=1; i<n; ++i)
		{
			if(mn[b[i]]>=i)cout<<n-sz[b[i]]<<" ";
			else cout<<"0 ";
		}
	cout<<n<<endl;
}

int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	ll t=1;
	//cin >> t;
	while (t--)
		{
			mysolve();
		}
	system("pause");
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值