首页 > 其他分享 >"蔚来杯"2022牛客暑期多校训练营7

"蔚来杯"2022牛客暑期多校训练营7

时间:2022-09-04 17:25:42浏览次数:77  
标签:前缀 int 蔚来 多校 牛客 数组 区间 长度 mod

C Constructive Problems Never Die

题意:

给你一个数组 A ,你需要构造一个排列 P ,使得P[i]≠A[i]

分析:

考虑构造不出来的情况 如果所有A[i]都相同一定不成立

先构造P[i]=i 如果P[i]=A[i] 就遍历一遍整个数组 找到一个P[j] 使得P[i] 和P[j] 交换之后分别都满足题意

复杂度:尽管是两层for循环 对于每个点i的最坏情况是第二层循环遍历完整个数组 这样原A数组数值都是i

第一层再遍历到其他点的时候 第二层循环只用遍历到第一个即可满足

#include <bits/stdc++.h>
using namespace std;
int a[100005],b[100005];
int main(){
	int t;
	cin>>t;
	while(t--){
		int n;
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			b[i]=i;
		}
		bool flag=1;
		for(int i=1;i<=n;i++){
			if(a[i]==b[i]){
				flag=0;
				for(int j=1;j<=n;j++){
					if(i!=j && a[i]!=b[j] && a[j]!=b[i]){
						swap(b[i],b[j]);
						flag=1;
					}
				}
				if(flag==0) break;
			}
		}
		if(flag){
			cout<<"YES\n";
			for(int i=1;i<=n;i++){
				cout<<b[i]<<" ";
			}cout<<endl;
		}else cout<<"NO\n";
	}
} 

F Candies

分析:

发现其实两种方式删除先后对于最后答案是不会产生影响的 所以直接暴力删除就好了

#include<bits/stdc++.h>
using namespace std;
int n,m,ans;
vector<int>a;

int main()
{
	cin>>n>>m;
	for(int i=1,x;i<=n;i++)
	{
		cin>>x;
		if(!a.empty()&&(a.back()==x||a.back()+x==m)) a.pop_back(),ans++;
		else a.push_back(x);
	}
	int l=0,r=a.size()-1;
	while(l<r&&(a[l]==a[r]||a[l]+a[r]==m)) ans++,l++,r--;
	cout<<ans<<"\n";
	return 0;
}

J Melborp Elcissalc

分析:

对于快速求区间和,采用前缀和算法

由于要求 k 的倍数,所以我们可以把前缀和 m o d k 容易想到,由取模而得到的前缀和是单一的,并且可以通过倒推得到

如何由前缀和快速判断是否优美

设sum为原数组的前缀和 区间[L,R]优美 当且仅sum[R]-sum[l-1]=0(mod k) 即sum[R]=sum[l-1](mod k)

前缀和为i的数有cnt个 通过组合数得到优美度为C(2,cnt)

那么因为数的范围为 [0,k-1],所以确定了模K的前缀和数组就可以唯一确定原数组

如何快速构造符合的数组

考虑DP解决 dp[i][j][k]表示用[0,i]的数 填充了j个位置 优美度为k 的数组数量

转移:

设加入的新数字的数量为 x ,剩余空位置有n-j个

dp[i+1][j+x][k+C(2,x)]+=dp[i][j][k]×C(x,n-j)

初始化 :

当i=0的时候需要特判不再是C(2,i) 而是C(2,i+1)

非常好的一道计数题目

#include<bits/stdc++.h>
using namespace std;
const int N=70;
const int mod=998244353;
int dp[N][N][N*N];
int C[N][N];
int inc[N],fac[N];
int n,t,k;
int ksm(int a,int b)
{
    int ret=1;
    while(b)
    {
        if(b&1)
            ret=1ll*ret*a%mod;
        a=1ll*a*a%mod;
        b>>=1;
    }
    return ret;
}
void init()
{
    for(int i=0;i<N;i++)               
    {
        C[i][0]=1;
        for(int j=1;j<=i;j++)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    }
}
int main()
{
    init();
    scanf("%d%d%d",&n,&k,&t);
    for(int i=0;i<=n;i++)                   
        dp[0][i][C[i+1][2]]=C[n][i];
    for(int i=0;i<k-1;++i)             
    {
        for(int j=0;j<=n;++j)
            for(int l=0;l<=t;++l)
            {
                if(!dp[i][j][l])
                    continue;
                for(int x=0;j+x<=n&&l+C[x][2]<=t;++x)
                    dp[i+1][j+x][l+C[x][2]]=(1ll*dp[i+1][j+x][l+C[x][2]]+dp[i][j][l]*C[n-j][x])%mod;
            } 
    }
    printf("%d",dp[k-1][n][t]);
}

K Great Party

显然,区间长度为1的时候,先手必胜。

如果区间长度为2。

两个人都不会执行合并操作的,因为这样对手就进入先手必胜态了。

如果两个数都相同的话,那么先手拿多少个石子,后手就在另外一堆拿相同数目的石子,这样就可以赖死先手。

所以区间长度为2,且两个数相同,先手必输

并且如果两个数不同,先手直接拿最大的那一堆,使得剩下的两个数相同,后手面对先手必输的局面。

所以两个数不同,先手必胜

如果区间长度为3,那么先手一定可以通过一次操作,带上合并,使得出现两个相同的数。

所以区间长度为3,先手必胜

如果区间长度为4,显然,二者都不可能在自己这一轮,拿完一堆石子。

不然对手就区间长度为3,获胜了。

所以大家拿到 [1,1,1,1]就不能再拿了。和Nim游戏对比 [0,0,0,0]不能拿到

所以区间长度为4就是执行 a[i]-1 的Nim游戏

综上:

区间长度为奇数时,先手必胜。

区间长度为偶数时,执行减一的Nim游戏。

我们只考虑先手必输的局面,就是区间长度为偶数,且减一的异或值等于0.

那么我们利用前缀异或和。区间 [L,R] 异或等于0,即 pre[L-1]==pre[R] 。

因为要求区间长度为偶数,所以我们直接将 pre[i]根据 i 的奇偶分开。

然后就是跑个莫队统计答案了

注意:异或和的数组要开大一点 !!!!

不用C(2,n) 直接每次莫队的时候加减当前有多少个即可!!!

#include<bits/stdc++.h>
using namespace std;
inline int in(){ // 快读 
	int x;
	scanf("%d",&x);
	return x;
}
const int N=1e5+5,M=1<<20|5,B=300;
int n,Q;
int a[N],s[N];
struct qes{
	int l,r,id;
}q[N];
inline bool cmp(qes a,qes b){
	int x=a.l/B,y=b.l/B;
	if(x==y)return a.r<b.r;
	return x<y;
}
long long res,ans[N];
int c[2][M];
void ins(int p){
	res += c[p&1][s[p]]++;
}
void del(int p){
	res -= --c[p&1][s[p]];
}
int main(){ // NIM变式+莫队 
	n=in(); Q=in();
	for(int i=1;i<=n;i++){
		a[i]=in()-1; // 要-1,每堆留下一个石子 
		s[i]=s[i-1]^a[i]; // 异或前缀和 
	}
	for(int i=1;i<=Q;i++){
		q[i].l=in()-1;
		q[i].r=in();
		q[i].id=i;
	}
	sort(q+1,q+Q+1,cmp);
	int L=1,R=0;
	for(int i=1;i<=Q;i++){
		int l=q[i].l,r=q[i].r;
		while(R<r)ins(++R);
		while(L>l)ins(--L);
		while(R>r)del(R--);
		while(L<l)del(L++);
		int len=r-l;
		ans[q[i].id]=(long long)len*(len+1)/2-res;
	}
	for(int i=1;i<=Q;i++)printf("%lld\n",ans[i]);
	return 0;
}


标签:前缀,int,蔚来,多校,牛客,数组,区间,长度,mod
From: https://www.cnblogs.com/wzxbeliever/p/16655463.html

相关文章

  • 牛客dfs专题 NC13594 选择困难症(dfs+剪枝)
    链接:https://ac.nowcoder.com/acm/problem/13594来源:牛客网题目描述有k类物品,每类物品的个数为Ai,每个物品有一个喜欢值Vj,代表小L对这件物品的喜欢程度。小L想知道,有多......
  • 牛客dfs专题:轰炸区最优选取(二维前缀和)
    链接:https://ac.nowcoder.com/acm/problem/14505来源:牛客网题目描述现在给出一个正方形地图,其边长为n,地图上有的地方是空的,有的地方会有敌人。我们现在有一次轰炸敌人......
  • 牛客练习赛102
    A清楚姐姐的学术群intmain(){ intn=read(),m=read(),a=read(),b=read(); for(inti=1;i<=n;i++)people[i].push_back(0); for(inti=1;i<=......
  • 29. 牛客-一人行者
    本来不想为了这题写一篇博客的,但因为昨天被一组数据卡了一个小时,还是有必要来记录一下。牛客练习赛102D:一人行者题意是给一棵树,求断掉每一条边后得到的两棵树各自的联通......
  • "蔚来杯"2022牛客暑期多校训练营6
    A.Array给定\(\geqslant2\)的整数\(a_1,a_2,...,a_n\),满足\(\sum\limits_{i=1}^n\frac{1}{a_i}\leqslant\frac{1}{2}\),构造一个循环数列,使得其任意长度为\(a_i\)的子区......
  • 牛客练习赛102 B-C
    B清楚姐姐带带我 当数大于1e9的时候就取模//#defineintllconstintN=1e5+10,mod=19980829;intn,m;voidsolve(){llres=0;boolflag......
  • 牛客练习赛102
    A对所有消息做一下前缀和,对每个人的消息做一下前缀和,分别判断是否有长度为\(a,b\)的连续段B考虑当前已经算出来前\(i-1\)个操作的最大值\(x\),那么第\(i\)个操作......
  • Fast Bubble Sort (单调zai+倍增) (2022杭电多校10)
    VirtualJudge(vjudge.net)题目:题目大意:首先说明一个性质,A表示一个数组,B(A)表示把这段数组进行一遍冒泡排序的下沉操作,就是把大数沉底。然后题目给我们一个长度为n的......
  • 2022牛客暑假多校01B[Spirit Circle Observation]
    2022牛客暑假多校01B[SpiritCircleObservation]大致题意给出一个长度为\(n\)的字符串\(s\),求有多少个子串对\((A,B)\),满足\(1.|A|=|B|\)\(2.\overline{A}+1=......
  • 2022 HDU多校10
    WinnerPrediction(网络流)Problem\(n\)个人进行比赛,赢最多的人获胜,保证一定可以分出胜负,现在已知\(m_1\)场对决结果,还有\(m_2\)场对决结果未知,但知道比赛的两个人是谁,问......