首页 > 其他分享 >Codeforces Round #885 数学专场

Codeforces Round #885 数学专场

时间:2023-07-24 22:13:33浏览次数:44  
标签:10 885 int bmod Codeforces ans oplus Round define

妙,我只能说妙。今天补DEF发现除了F诡秘的杨辉三角,我都能独立做出来。

但为什么我感觉DE难度不如C!!!!

A

题意:一个人站在 \((x,y)\) 处,而其他人分别在 \((x_1,y_1)\dots (x_n,y_n)\),每一次这个人先移动一步到上下左右四个格子,然后其他 \(n\) 个人再移动一步,求是否永远这个人与其他人不会走到一个位置。

题解:

对于永远等敏感字眼,我们一般是找题目中的不变量亦或者变量之间的关系

在这个题中,注意到每移动一步之后,每个人的横纵坐标和的奇偶性都会改变,所以若两个人最初坐标奇偶性不同,则永远无法相遇,反过来,两个人最初坐标一致,则无论前者怎么走,后者都有走法使得曼哈顿距离不增大,而前者撞墙之后,后者就会缩小距离,最终相遇。

所以若存在 \(x_i+y_i\bmod 2=x+y\bmod 2\),则输出 “NO”,否则输出“YES”。

int n,x,y,k,m,a[105][105];
signed main(){
	int t;cin>>t;
	while(t--){	
		cin>>n>>m>>k>>x>>y;
		int tag=0,s=(x+y)&1;
		for(int i=1;i<=k;i++){
			int x,y;cin>>x>>y;
			if(((x+y)&1)==s)tag=1;
		}
		if(tag)cout<<"No\n";
		else cout<<"Yes\n";
	}
}

B

题意:有 \(n\) 块木板排成一排,第 \(i\) 块木板的颜色是 \(c_i\),你站在第一块木板前面,需要跳跃到第 \(n\) 块木板后面,每一次只能跳相同颜色的木板。现在你可以更改一块木板的颜色,使得你每一次跳跃的距离(指两块木板中间部分,不计两端点)的最大值最小。

“最大的最小”却不是二分答案,有意思。

在本题中,由于每次只能跳同一个颜色,必然是每一个颜色单独处理。

先将所有点按颜色为第一关键字,下标为第二关键字进行排序,将同色木板分成一组,考虑单独处理这一组。

设这一组木板下标分别为 \(a_1,a_2…a_t(a_1<a_2<\dots <a_t)\),则每两块木板间的距离为 \(a_i-a_{i-1}-1\),特别地,最后一块木板里边界的距离为 \(n-a_t\)。

记 \(d_i=a_i-a_{i-1}\),特别地,\(d_{t+1}=n-a_t\)。

先考虑不重新涂色,答案为 \(\max_{i=1}^{t+1}\lbrace d_i\rbrace\),若重新涂色,则必定是在 \(d_{\max}\) 所代表的两块木板中间,以消去这个最大值,则 \(d'_{\max}=\left\lceil \frac{d_{\max}}{2}\right\rceil\),最后再取最大值即可。

那么对于每个颜色都这样处理,最后取最小就是答案。

#define N 505050
#define int long long
#define pr pair<int,int>
#define mk make_pair
int n,x,y,k,m,pre[N],cnt[N];
pr c[N];
int solve(int l,int r){
	int tot=0;
	for(int i=l;i<=r;i++)cnt[++tot]=c[i].second;cnt[++tot]=n+1;
	int ans=0,mx=0,cmx=0;
	for(int i=tot;i;i--)cnt[i]=cnt[i]-cnt[i-1];
	for(int i=1;i<=tot;i++){
		mx=max(mx,cnt[i]);
	}
	for(int i=1;i<=tot;i++){
		if(mx==cnt[i]){
			cnt[i]=(cnt[i]+1)/2;mx=-1;
		}
		ans=max(ans,cnt[i]);
	}
	return ans-1;
}
signed main(){
	int t;cin>>t;
	while(t--){	
		read(n);int ans=0x3f3f3f3f,s;read(s);
		for(int i=1;i<=n;i++)read(c[i].first),c[i].second=i;
		sort(c+1,c+n+1);int l=0,r=0;c[0]=c[n+1]=mk(0,0);
		for(int i=1;i<=n;i++){
			if(c[i].first!=c[i-1].first)l=i;
			if(c[i].first!=c[i+1].first){
				ans=min(ans,solve(l,i));
			}
		}
		cout<<ans<<"\n";
	}
}

C

题意:

给定数组 \(a,b\),定义一次操作如下:

令 \(c_i=|a_i-b_i|\),得到 \(a'_i=b_i,b'i=c_i\)。

求是否可能通过 \(k\) 次操作,使得所有的 \(a_i\) 都为零。如果可能,输出“YES”,否则输出“NO”。

首先,可以发现每一个 \(i\) 是独立的。(分离独立项原则

则我们可以考虑求出对于每一个 \(i\) ,\(ans_i\) 所需要满足的条件,最后判断是否存在通解即可。

现在我们来求解 \(ans_i\),将 \(a_i,b_i\) 简记为 \(a,b\)

设 \(f_0=a,f_1=b,f_i=|f_{i-2}-f_{i-1}|(i\ge 2)\),则在第 \(k\) 次操作后,\(a\) 的值为 \(f_{k}\)。

注意到若 \(b\) 远大于 \(a\),不妨设 \(b=ax+m(0\le m<a)\),则操作序列呈:

\[a,ax+m,a(x-1)+m,a,a(x-2)+m,a(x-3)+m,a…… \]

不难发现规律,若 \(x\bmod 2=1\),则操作到 \(m,a\) 状态时需要 \(\lfloor\frac{3}{2}x\rfloor\) 次操作,而 \(x\bmod 2=0\),则操作到状态 \(a,m\) 需要 \(\frac{3}{2}x\) ,此时问题化为了同样性质的子问题,启发我们递归求解答案。

这个过程类似于欧几里得算法,不难写出如下递归函数:

int gcd(int a,int b){
    if(a==0)return 0;
    if(b==0)return 1;
    if(a>=b){
        int r=a%b,k=a/b;
        if(k%2==1)return gcd(b,r)+k+k/2;
		else return gcd(r,b)+k+k/2;
    }
    return 1+gcd(b,abs(a-b));
}

注意到在最后 \(a=0\) 后,整个循环节长度变为 \(3\),这就给了我们求通解的机会。

\(ans_i=x_i+3k(k\in\mathbb{N})\),其中 \(x_i\) 是第一次 \(a=0\) 时候的操作数。

则有通解的充要条件显然是所有的 \(x_i\) 模三同余。

注意 \(a_i=b_i=0\)需要特判。

D

找循环节,不变量是解决数学题的有力手段

题意:

给定数 \(a\) 和 操作数 \(k\),每一次可以进行两个操作:

  1. 将 \(a\) 加上其个位数字。
  2. 将 \(a\) 累加到答案中。

求答案的最大值。

贪心策略:所有操作一必然是在最开始进行的,证明显然。

考虑特殊性质:

  1. \(a\bmod 10=5\),此时最多进行一次操作1。
  2. \(a\bmod 10\) 为偶数,此时 \(a\) 的个位数字随着不断地操作1最后回归到 \(2,4,8,6\) 的循环中。
  3. \(a\bmod 10\) 为奇数且不等于 \(5\),此时进行一次操作1即可化为情况2。

对于第一种情况,答案显然为 \(\max(ak,(a+5)(k-1))\)。

对于第二种情况。设最优情况下操作1进行的次数 \(c=4x+r(0\le r<4)\),则可以枚举 \(r\),最多枚举四个,顺带更改 \(a,k\),则化为求 \((a+20x)(k-4x)\) 的最值。这显然是个二次函数,利用顶点坐标公式即可求解。

第三种情况可以转化为第二种情况,在此略去。

#define int long long
signed main(){
	int t,a,k;
	cin>>t;
	while(t--){
		cin>>a>>k;
		if(a%10==0){
			cout<<a*k<<"\n";continue;
		}
		if(a%10ll==5ll){
			cout<<max(a*k,(a+5ll)*(k-1ll))<<"\n";continue; 
		}
		int ans=a*k;
		if(a&1ll){
			a+=a%10ll;k--;ans=max(ans,a*k);
		}
		for(int i=0;i<4;i++){
			if(!k)break;
			int aa=-80ll,bb=20ll*k-4ll*a,cc=a*k,x=-1*bb/(2ll*aa);
			if(x<0)x=0;
			x=min(x,k/4);
			ans=max(ans,x*x*aa+bb*x+cc);
			if((x+1)*4<=k){
				x++;ans=max(ans,x*x*aa+bb*x+cc);
			}
			a+=a%10ll;k--;
		}
		cout<<ans<<"\n";
	} 
}

E

挺有意思的问题,但个人觉得赛场上开题顺序如果是 ABEDCF 可能就不会是现在这悲催的结局了。

注意到:

\[ans_i=\sum_{x=1}^{\infty}\sum_{y=x}^{\infty}[\sum_{i=x}^yi=X_i]=\sum_{x=1}^{\infty}\sum_{y=x}^{\infty}[(x+y)(y-x+1)=2X_i] \]

这里长得有点像约数,但不完全是约数。

注意到 \(x+y,y-x+1\) 的奇偶性不同,而若确定了 \(x+y,y-x+1\) 则必定可以求出合法的 \(x,y\),\(2X_i\) 也是偶数,那么其一对奇偶不同的约数即为一组解。我们可以考虑将 \(2\) 强制乘在其中一个因子上,保证其为偶数。

则 \(ans_i\) 实际等于 \(X_i\) 的奇因子个数。

考虑如何求出 \(X_i\) 的奇因子个数,这等价于 \(X_i\) 去掉质因子2后的约数个数。记 \(e_i\) 为 \(x_i\) 去掉质因子2后的数,则 \(X_i\) 去掉质因子2后的数为 \(c_i=\prod_{k=0}^ie_i\),\(ans_i=d(c_i)\)。

直接求显然不现实,可以考虑维护每个质因子出现次数 \(f_i\),则 \(c_i=\prod (f_k+1)\)。

这是一个递推的过程,可以对 \(e_i\) 进行分解质因数来完成 \(f\) 的更新和答案计算,注意 \(x_0\) 的范围是 \(10^9\)。可以写出如下代码:

#include<iostream>
using namespace std;
#define N 1005050
#define int long long
int f[N],n,p,x,ans=1;
int power(int a,int b){
	int ans=1;
	while(b){
		if(b&1)ans=ans*a%p;
		a=a*a%p;
		b>>=1; 
	} 
	return ans;
}
int solve(int d){
	while((d&1)==0)d>>=1;
	for(int i=2;i*i<=d;i++){
		int cnt=0;
		while(d%i==0){
//			cout<<"prime: "<<i<<"\n";
			d/=i;++cnt;
		}
		if(cnt){
			if(!f[i]){
				ans=ans*(cnt+1)%p;f[i]=cnt;
			}
			else ans=ans*power(f[i]+1,p-2)%p*(f[i]+cnt+1)%p,f[i]+=cnt;
		}
	}
	if(d>1){
		if(d<=1e6){
			int i=d,cnt=1;
			if(!f[i]){
				ans=ans*(cnt+1)%p;f[i]=cnt;
			}
			else ans=ans*power(f[i]+1,p-2)%p*(f[i]+cnt+1)%p,f[i]+=cnt;
		} 
		else ans=ans*2ll%p; 
	}
	return ans;
}
signed main(){
	cin>>x>>n>>p;
	solve(x);
	for(int i=1;i<=n;i++){
		int d;cin>>d;solve(d);
		cout<<ans<<"\n";
	}
}

但是交上去挂了,为什么?

模数不固定,虽然是质数但可能很小,极有可能不存在逆元。

怎么办?发现我们只需要维护 \(f\) 的连乘积以及对 \(f\) 的单点修改,这完全可以使用线段树。

注意这里的时间复杂度是不正确的(\(O(n\sqrt n\log n)\)),但实际远远跑不满,再加上将 \(10^6\) 的值域范围先筛一遍质数变成 \(10^5\) 后卡卡可以过。(\(10^6\) 内有 \(78498\) 个质数)。

这里容易被大质数卡得分解过程爆炸,可以特判一下。

#pragma GCC opzitime(3)
#include<iostream>
using namespace std;
#define N 1005050
#define int long long
int f[N],n,p,x,ans=1,pri[N],v[N],tot,id[N];
struct node{
	int l,r,mul;
}t[N<<2];
#define lc x<<1
#define rc x<<1|1
void read(int &x){
	x=0;char ch=getchar();
	while(ch>'9'||ch<'0'){
		ch=getchar();
	}
	while('0'<=ch&&ch<='9'){
		x=(x<<3)+(x<<1)+(ch^48);ch=getchar();
	}
}
void build(int x,int l,int r){
	t[x].l=l,t[x].r=r,t[x].mul=1;
	if(l==r)return ;
	int mid=l+r>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
}
void updata(int x,int pos,int k){
	if(t[x].l==t[x].r){
		t[x].mul+=k;
		t[x].mul%=p;
		return ;
	}
	if(t[lc].r>=pos)updata(lc,pos,k);
	else updata(rc,pos,k);
	t[x].mul=t[lc].mul*t[rc].mul%p;
}
void solve(int d){
	while((d&1)==0)d>>=1;
	if(d<=1e6&&v[d]==0){
		updata(1,id[d],1);return ;
	}
	for(int i=2;i<=tot;i++){
		int cnt=0;
		while(d%pri[i]==0){
			d/=pri[i];++cnt;
		}
		updata(1,i,cnt);
		if(d<pri[i])break;
		if(pri[i]*pri[i]>d){
			if(d>1e6)ans=2;
			else if(v[d]==0){
				updata(1,id[d],1);
			}
			break;
		}
	}
}
void init(){
	v[1]=1;
	for(int i=2;i<=1e6;i++){
		if(!v[i]){
//			cout<<"pri: "<<i<<"\n";
			pri[++tot]=i;id[i]=tot;
		}
		for(int j=i;j<=1e6;j+=i){
			if(i!=j)v[j]=1;
		}
	}
}
signed main(){
	read(x),read(n),read(p);
	init();
	build(1,1,tot);
	solve(x);
	for(int i=1;i<=n;i++){
		int d;read(d);solve(d);
		printf("%lld\n",ans*t[1].mul%p);
	}
}

F

现在以 \(a_0\) 经过操作的变化为例,依次呈现出如下变化:

\(0: a'_0=a_0\)

\(1: a'_0=a_0\oplus a_1\)

\(2: a'_0=a_0\oplus a_2\)

\(3: a'_0=a_0\oplus a_1\oplus a_2\oplus a_3\)

\(4: a'_0=a_0\oplus a_4\)

\(5: a'_0=a_0\oplus a_1 \oplus a_4 \oplus a_5\)

\(6: a'_0=a_0\oplus a_2 \oplus a_4 \oplus a_6\)

\(7: a'_0=a_0\oplus a_1\oplus a_2\oplus a_3 \oplus a_4 \oplus a_5 \oplus a_6\oplus a_7\)

\(8: a'_0=a_0\oplus a_8\)
……
如果说,我们写一个系数矩阵 \(b\) (\(n=8\)),有:

\[\left[ \begin{matrix} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 1 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 1 & 1 & 1 & 1 & 0 & 0 & 0 & 0 \\ 1 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\ 1 & 1 & 0 & 0 & 1 & 1 & 0 & 0 \\ 1 & 0 & 1 & 0 & 1 & 0 & 1 & 0 \\ 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \end{matrix} \right] \]

观察到什么了吗?\(b_{i,j}=(b_{i-1,j-1}+b_{i-1,j})\bmod 2\)。事实上这是一个在模2意义下的杨辉三角。

而在最后一行,变成了自己异或自己,则必然有解。-1就是骗人的。。。

我们考虑求出这个最小的解。显然当全部为0之后,后面无论执行多少次操作都是全0。

那么这个值是可二分的,换句话说是可倍增的(题目更适用倍增一些)。

我们考虑从大到小枚举增量 \(p=2^k\),最初 \(p=n\),后不断除2。

考虑求出上一次操作到 \(p\) 次操作之后的数组 \(b\),显然根据表格有 \(a'_i=a_i\oplus a_{i\oplus p}\)。

然后判断是否全0即可解决这个问题。复杂度 \(O(n\log_2 n)\)。

有没有什么优化空间呢?

注意到每次“有效”的倍增更改后,\(i,i\oplus p\) 这两个位置的数其实都是 \(a_i\oplus a_{i\oplus p}\),换言之这个数组是前后相等的,我们只需保留一半继续处理即可。

时间复杂度 \(O(n)\)。

#include<iostream>
using namespace std;
#define N (1<<21)
int a[N],b[N],n;
int main(){
	ios::sync_with_stdio(false);
	cin>>n;for(int i=0;i<n;i++)cin>>a[i];
	int mx=0;for(int i=0;i<n;i++)mx=max(mx,a[i]);
	if(!mx){
		cout<<"0\n";return 0;
	}
	int ans=0;
	while(n>1){
		int m=n>>1;
		for(int i=0;i<m;i++)b[i]=a[i]^a[i+m];
		mx=0;for(int i=0;i<m;i++)mx=max(mx,b[i]);
		if(mx){
			ans|=m;for(int i=0;i<m;i++)a[i]=b[i];
		}
//		else break;
		n=m;
	} 
	cout<<ans+1<<"\n";
} 

标签:10,885,int,bmod,Codeforces,ans,oplus,Round,define
From: https://www.cnblogs.com/oierpyt/p/17578484.html

相关文章

  • Codeforces Round 887 (Div. 2)记录
    A.Desorting如果有$a_1\leqa_2\leq\ldots\leqa_{n-1}\leqa_n$,则称长度为$n$的数组$a$已排序。Ntarsis有一个长度为$n$的数组$a$。他可以对数组进行一种操作(0次或多次):-选择一个索引$i$($1\leqi\leqn-1$)。-将$1$加到$a_1,a_2,\ldots,a_i$。-用$a_{......
  • Codeforces Round 887(Div 2)(A-C)
    A.Desorting题目里说的无序是指后面的一个数大于前面一个数,所以只要有一个a[i+1]-a[i]<0就说明已经符合题目的无序要求了,不需要改变即可,即输出0如果有该序列是非严格递增的根据题目所说的改变就只需要求出最小的差值即可,最后用最小的差值除以2(因为每次可以让选中的部分之前的......
  • 【题解】Imbalanced Arrays - Codeforces 1852B
    出处:CodeforcesRound887链接:https://codeforces.com/problemset/problem/1852/B题目大意:给定一个包含\(n\)个非负整数的频次序列\(f\)。构造任意一个等长的整数序列\(b\),要求①\(b\in[-n,n]\)but$b\neq0$②\(b\)中不存在相反数③对于每个坐标\(i\)......
  • Codeforces Round 886 (Div. 4)
    Dashboard-CodeforcesRound886(Div.4)-CodeforcesA.ToMyCritics判断任意两个大于10即可#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintN=2e5+10;signedmain(){ios::sync_with_stdio(false);cin.tie(nullptr);......
  • 【题解】Ntarsis' Set - Codeforces 1852A
    出处:CodeforcesRound887链接:https://codeforces.com/problemset/problem/1852/A题目大意:给定一个包含\(n\)个正整数的表示删除位置的严格升序序列\(p\),以及另外一个连续正整数的被删除的无穷序列\(l\)。进行\(k\)次删除操作,每次操作从无穷序列\(l\)中同时删除位......
  • Educational Codeforces Round 71
    A.ThereAreTwoTypesOfBurgers#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongvoidsolve(){intb,p,f,h,c;cin>>b>>p>>f>>h>>c;b/=2;intres=0;for(inti......
  • Codeforces Round 887 (Div. 1) 题解
    https://codeforces.com/contest/1852/problemsA.Ntarsis'Sethttps://codeforces.com/contest/1852/problem/A感觉不是很一眼。\(n\)和\(k\)都是\(2\times10^5\),不能暴力,设当前集合为\({1,2,\dots,10^{1000}}\),那么被操作过一次的最小值就应该是\(\text{MEX}(0,......
  • CodeForces 1810G The Maximum Prefix
    洛谷传送门CF传送门感觉是比较educational的题。拿到题目应该有一个大致思路,就是考虑最大前缀和的求法,再把它扔到状态里面。最大前缀和有两种求法:从前往后求,需要维护当前前缀和\(s\),当前最大前缀和\(mx\),需要记录两个变量,无法承受。从后往前求,只需记录当前最大前缀和......
  • 「题解」Codeforces Round 887 (Div. 2)
    A.DesortingProblem题目Sol&Code若序列一开始无序答案为\(0\)若有序即\(a_1\leqa_2\leq\dots\leqa_n\)。若想让\(a_i>a_j(i<j)\),操作次数与两数差值\(d(d=a_j-a_i)\)相关为\(\lfloor\dfrac{d}{2}\rfloor+1\),差值越小操作次数越少,故枚举相邻两数取最少......
  • Codeforces Round 887 (Div. 2)
    C.Ntarsis'Set​ (\(1\leqn,k\leq2\cdot10^5\))题解:思维+二分我们不妨反向考虑由于答案最后一次一定在第一个位置所以答案上一轮一定在第一个空的位置,假设这个位置为\(x\)那么如果说要使得答案在位置\(x\),那么上上一轮答案一定在第\(x\)个空的位置我们可以......