Dashboard - 2022 CCPC Henan Provincial Collegiate Programming Contest - Codeforces
Problem B. Hash
思路:
- 发现31的次幂取模的答案,所以如果一段太长肯定不如拆成2段。
- 首先如果一段长度为7,那么无论他的开头是a,eh,n的谁,都有val>=31^6=887503681>P/2
- 所以我一定可以把长度大于等于14的拆成2段,使得val(7)+val(7)>P>val(14)
- 明确每一段长度才这么一点,我们可以暴力枚举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 的试卷答案
思路:
- 对于s[i]>s[i+1]的,显然我们必须把他们隔开。
- 而我们需要获取k段,显然需要插k-1个隔板,如果我们把必须隔开的隔开后,剩下可以插空的位置就是可以容易插。所以对于一段[l,r]划分k段,要求必须划分的有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
思路:
- 如果mex=n,显然答案为n
- 我们以val[i]=0的i设为根rt
- 显然mex=0时答案就是根节点的最大子树
- 其他情况,mex=k,显然val[i]=k的点不能包含,又必须包含小于k的所有点,显然我们答案就是取除k的子树之外的所有点(因为0点为根节点,k子树内的点与0相连必须经过k),如果k子树内存在点小于k,那么答案为0,如果都大于k,那么答案为n-sz[k](k子树大小)
- 因此,我们需要维护每个点的子树的包含的最小值
#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;
}