title :2022牛客暑期多校训练营7 题解
date : 2022-9-22
tags : ACM,练习记录
author : Linno
2022牛客暑期多校训练营7 题解
题目链接 :https://ac.nowcoder.com/acm/contest/33192
补题进度 :5/12
文章目录
C-Constructive Problems Never Die
如果每个数都是一样的,那么才能是no,否则肯定可以找到一种排序满足。没出现过的数可以可以随便填,尽量想办法填满相同的数字,剩下的数错排就行了。
#include <bits/stdc++.h>
using namespace std;
int T,n,cnt,b[100005],a[100005],que[100005],vis[100005],sum,head,v[100005];
queue<int>q;
int main()
{
cin>>T;
while (T--)
{
scanf("%d",&n);
for (int i=1;i<=n;++i) vis[i]=v[i]=a[i]=b[i]=0;
cnt=1;
for (int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
if (i>1&&a[i]!=a[i-1]) cnt=0;
}
if (cnt==1) { printf("NO\n"); continue; }
for (int i=1;i<=n;++i)
{
if (vis[a[i]]==0&&a[i]*2!=n+1) { b[i]=n-a[i]+1; vis[a[i]]=1; v[n-a[i]+1]=1; }
else b[i]=0;
}
while (!q.empty()) q.pop();
for (int i=1;i<=n;++i)
if (v[i]==0) { q.push(i); }
for (int i=1;i<=n;++i)
if (b[i]==0)
{
while (!b[i])
{
int x=q.front();
q.pop();
if (x!=a[i]||q.empty()) b[i]=x;
else q.push(x);
}
}
printf("YES\n");
for (int i=1;i<=n;++i)
if (b[i]==a[i])
for (int j=1;j<=n;++j)
if (b[i]!=a[j]&&b[j]!=a[i]) { swap(b[i],b[j]); break; }
for (int i=1;i<=n;++i)
printf("%d ",b[i]);
printf("\n");
}
return 0;
}
F-Candies
贪心地能删就删,直观能感受到顺序不影响答案。
#include <bits/stdc++.h>
using namespace std;
struct dat
{
int v,l,r;
}a[1000005];
int n,m,cnt;
int main()
{
cin>>n>>m;
for (int i=1;i<=n;++i)
{
scanf("%d",&a[i].v);
a[i].l=i-1;
a[i].r=i+1;
}
a[1].l=n; a[n].r=1;
int i=1; int ans=0; int cnt=0;
while (n>=2&&cnt<=n)
{
if (a[i].v+a[a[i].r].v==m)
{
ans++;
a[a[i].l].r=a[a[i].r].r;
a[a[a[i].r].r].l=a[i].l;
i=a[i].l;
n-=2;
cnt=0;
}
else if (a[i].v==a[a[i].r].v)
{
ans++;
a[a[i].l].r=a[a[i].r].r;
a[a[a[i].r].r].l=a[i].l;
i=a[i].l;
n-=2;
cnt=0;
}
else { i=a[i].r; ++cnt; }
}
printf("%d\n",ans);
return 0;
}
G-Regular Expression
读懂样例就能过了,然后大概看代码就能懂了。大概就是分类讨论,如果长度为一,比如a显然是 a 和 . a和. a和.。除此之外的情况,如ab的答案就有 a b / a . / . b / . ∗ / . + / . . ab/a./.b/.*/.+/.. ab/a./.b/.∗/.+/..六种。如果字符串只有一种字符,那么答案加上 a + 和 a ∗ a+和a* a+和a∗都是可以的。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N=2e6+7;
const int mod=1e9+7;
string str;
void Solve(){
cin>>str;
int len=str.length();
if(len>=3){
int fg=1;
for(int i=1;i<len;++i){
if(str[i]!=str[i-1]){
fg=0;
break;
}
}
if(fg) cout<<2<<" "<<4<<"\n"; //+和*
else cout<<2<<" "<<2<<"\n"; //.*和.+
}else if(len==1){
cout<<1<<" "<<2<<"\n";
}else if(len==2){
if(str[0]==str[1]){
cout<<2<<" "<<8<<"\n";
}else cout<<2<<" "<<6<<"\n";
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("in.cpp","r",stdin);
// freopen("out.cpp","w",stdout);
int T=1;
cin>>T;
// clock_t start,finish;
// start=clock();
while(T--){
Solve();
}
// finish=clock();
// cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;
return 0;
}
J-Melborp Elcissalc
d
p
[
i
]
[
j
]
[
k
]
dp[i][j][k]
dp[i][j][k]表示前
i
i
i个数中填了
j
j
j个位置且合法数为k的方案数,转移方程为:
∑
s
=
0
j
d
p
[
i
−
1
]
[
j
−
s
]
[
k
−
C
s
2
]
∗
C
n
−
j
+
s
s
,
C
为组合数,
s
为前缀和
\sum_{s=0}^jdp[i-1][j-s][k-C_s^2]*C_{n-j+s}^s,C为组合数,s为前缀和
s=0∑jdp[i−1][j−s][k−Cs2]∗Cn−j+ss,C为组合数,s为前缀和
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
typedef long long ll;
ll n,k,t;
ll qpow(ll a,ll b) {
ll ans=1;
while(b) {
if(b&1) {
ans=(ans*a)%mod;
}
a=(a*a)%mod;
b>>=1;
}
return ans;
}
ll sum[70];
ll fac[70];
ll ifac[70];
ll cc[70][70];
ll dp[66][66][3000];
int main () {
cin>>n>>k>>t;
sum[0]=0,fac[0]=1;
for(int i=1; i<=66; ++i)
sum[i]=sum[i-1]+i,fac[i]=(fac[i-1]*i)%mod;
ifac[1]=1;
ifac[0]=1;
for(int i=2; i<=66; ++i)
ifac[i]=qpow(fac[i],mod-2);
for(int i=1; i<=66; ++i) {
for(int j=0; j<=i; ++j) {
cc[i][j]=fac[i]*ifac[j]%mod*ifac[i-j]%mod;
}
}
cc[0][0]=1;
for(int i=0; i<=n; ++i) {
dp[0][i][(i*i+i)/2]=cc[n][i];
}
for(int i=1; i<k; ++i) {
for(int j=0; j<=n; ++j) {
for(int cnt=0; cnt<=(j*j+j)/2; ++cnt) {
for(int p=0; p<=n-j; ++p) {
dp[i][j+p][cnt+(p*p-p)/2]=(dp[i][j+p][cnt+(p*p-p)/2]+dp[i-1][j][cnt]*cc[n-j][p]%mod)%mod;
}
}
}
}
cout<<dp[k-1][n][t];
return 0;
}
K-Great Party
对于奇数长度的区间,显然必胜;对偶数长的区间,谁让堆数变为奇数就输了,考虑双方最后的策略会使得最后堆数不变,每堆石子数量变为1,因此就变成了尼姆博弈。题意转变为:求区间内异或和为0或长度为奇数的子区间个数。对于多次查询,我们采用莫队,每次暴力计算插入/删除一个异或前缀和时对答案的贡献。
#include<bits/stdc++.h>
using namespace std;
struct dat
{
long long l,r,id,blo;
}b[1000005];
long long n,m,blo,a[1000005],cnt[2][3000005],s[1000005];
long long ans[1000005],now;
bool cmp(dat a,dat b)
{
if (a.blo!=b.blo) return a.blo<b.blo;
else if (b.blo&1) return a.r>b.r;
else return a.r<b.r;
}
void add(int x)
{
int p;
int y=a[x];
if (x&1) p=1; else p=0;
now+=cnt[p][y];
cnt[p][y]++;
}
void del(int x)
{
int p;
int y=a[x];
if (x&1) p=1; else p=0;
cnt[p][y]--;
now-=cnt[p][y];
}
int main()
{
cin>>n>>m;
//int blo=pow(n,0.66);
//blo=int(ceil(pow(n,0.66)));
blo=n/sqrt(m);
if (blo<=0) blo=1;
for (int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
a[i]=(a[i]-1)^a[i-1];
}
for (int i=1;i<=m;++i)
{
scanf("%d%d",&b[i].l,&b[i].r);
b[i].l--;
b[i].blo=(b[i].l-1)/blo;
b[i].id=i;
}
sort(b+1,b+1+m,cmp);
//cnt[1][a[1]]++;
int L=1; int R=0;
for (int i=1;i<=m;++i)
{
while (L>b[i].l) add(--L);
while (L<b[i].l) del(L++);
while (R>b[i].r) del(R--);
while (R<b[i].r) add(++R);
long long len=(b[i].r-b[i].l);
long long all=(len*(len+1ll))/2;
ans[b[i].id]=all-now;
}
for (int i=1;i<=m;++i)
printf("%lld\n",ans[i]);
return 0;
}