9.1:

Contest by Dream__Sky

T1:ARC227A 没什么好说的

【其他题&思路暂时被数据删除了(我没保存qwq)】

9.2:

Contest by:梦熊

T1:CF1806B(无多测版)

分类讨论小清新题

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
int s,n,a[N];
signed main(){
	freopen("arrange.in","r",stdin);
	freopen("arrange.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++) {
		cin>>a[i];
		if(a[i]==0) s++;
	}
	if((!(n%2)&&s<=n/2)||((n&1)&&s<=(n+1)/2)) cout<<0<<'\n';
	else{
		if(s==n) cout<<1<<'\n';
		else{
			for(int i=1;i<=n;i++)
				if(a[i]>1){
					cout<<1<<'\n';
					exit(0);
				}
			cout<<2<<'\n';
		}
	}
} 

T2:CF1780B(无多测版)

也是小清新

对于三个正整数 \(a, b, c\),有 \(gcd(a+b,c) > gcd(a,b,c)\)

证明也是容易的,设 \(q = gcd(a, b)\),有\(a = q \times m\)\(b = q \times n\) 其中 \(gcd(m,n)=1\)

相加得

\[a+b = q \times m + q \times n = q \times (m+n)) \]

所以得 \(q | (a+b)\),即 \(gcd(a+b,c) > q\) 显然成立。

这个性质说明最优划分一定只会划分为两段,否则将相邻的两段合并起来一定更优。

于是滚一个前缀和,暴力枚举分割点即可,时间复杂度 \(\mathcal{O}(n log V)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace Cosmic{
	const int N=1e5+10;
	int n,ans,a[N],b[N];
	inline void fre(){freopen("partition.in","r",stdin);freopen("partition.out","w",stdout);}
	inline void solve(){
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			b[i]=b[i-1]+a[i]; 
		}
		for(int i=1;i<=n-1;i++) ans=max(ans,__gcd(b[i],b[n]-b[i]));
		cout<<ans;
	}
}using namespace Cosmic;
signed main(){
	fre();
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int T=1;
	while(T--) solve();
}

T3:CF1303D (无多测版)

(赛时题目是切蛋糕,这里统一也是切蛋糕,原题是放盒子)

首先无解是好判断的,所有蛋糕的和小于 \(n\) 就无解,否则在最坏情况下都可以把蛋糕都切成大小为 \(1\) 然后拼起来。

考虑到 \(n\) 很大,而且 \(a_i\) 有一个 \(2\) 的幂次的限制,不如在二进制下考虑问题。

因为把高位的 \(1\) 往下一位拆会有 \(1\) 的代价,而低位的 \(1\) 向高位的合成是免费的,于是从低位向高位贪
心。

假设现在贪心到了第 \(k\) 位(\(n\) 的二进制表示下第 \(k\) 位为 \(1\)),如果当前存在一块蛋糕大小为 \(2^k\),那么直接用了,剩下的蛋糕向高位合成(两块大小为 \(2^k\) 的蛋糕可以当成一块大小为 \(2^{k+1}\) 的蛋糕),以保证蛋糕的充分利用。

如果不存在,怎么办呢?考虑将大的蛋糕往下面拆,找到最小的一块比 \(2^k\) 大的蛋糕,往下一直切,直到切出一块大小为 \(2^k\) 的蛋糕即可。这样贪心的正确性显然,时间复杂度 \(\mathcal{O}(m+log^2n)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace Cosmic{
	const int N=1e5+10;
	int m,count,two[65],b[65],a[N],n,ans;
	bool f[65];
	inline void fre(){freopen("cake.in","r",stdin);freopen("cake.out","w",stdout);}
	inline void solve(){
		cin>>m>>n;
		count=0;
		for(int i=1;i<=n;i++) cin>>a[i],count+=a[i];
		int ans1=0,ans2=0;
		if(count<m){
			cout<<-1;
			exit(0);
		}
		for(int i=1;i<=n;i++){
			int x=log(a[i])/log(2);
			b[x]++;
		}
		ans=0;
		for(int i=0;i<=62;i++){
			if((1ll<<i)&m) f[i]=1;
			else f[i]=0;
		}
		two[0]=b[0];
		for(int i=1;i<=62;i++)two[i]=two[i-1]+1ll*((1ll<<i)*b[i]);
		int sum=0;
		for(int i=0;i<=62;i++){
			if(f[i]){
				if((1ll<<i)>two[i]-sum){
					for(int j=i+1;j<=62;j++)
						if(b[j]){
							ans+=j-i;
							b[j]--,b[i]+=2;
							for(int k=i+1;k<j;k++) b[k]++;
							break;
						}
				}
				two[0]=b[0];
				for(int j=1;j<=62;j++) two[j]=two[j-1]+1ll*((1ll<<j)*b[j]);
				sum+=(1ll<<i);
			}
		}
		cout<<ans;
	}
}using namespace Cosmic;
signed main(){
	fre();
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int T=1;
	while(T--) solve();
}

T4:

(找不到原题,故在此把题面复制一下)

矩阵(matrix)

题目描述

小 C 有一个大小为 \(n\times m\)01 矩阵 \(A\)

小 C 认为第 \(i\) 列是好的当且仅当第 \(i\) 列中 1 刚好出现了一次,即 \(\sum_{j=1}^n [A_{j,i}=1]=1\)

小 C 可以进行以下操作任意次:选择矩阵 \(A\) 中的某一行将其 01 翻转(即 0 变成 11 变成 0)。

小 C 想要让矩阵 \(A\) 中好的列数尽可能多,你能告诉他这个最大值吗?

输入格式

第一行输入两个数字 \(n,m\),分别表示矩阵的长与宽。

接下来 \(n\) 行,每行包含一个长度为 \(m\) 仅由 01 组成的字符串。

输出格式

共一行,输出一个整数,表示矩阵 \(A\) 中最多的好的列数。

样例 1 输入

3 4
0101
0110
1011

样例 1 输出

3

样例 1 解释

将每一行都进行翻转,矩阵 \(A\) 变为:

1010
1001
0100

此时第 \(2,3,4\) 列是好的,故答案为 \(3\)

样例 2 输入

3 3
101
111
000

样例 2 输出

2

其余样例见下发文件。

数据规模与约定

  • 对于 \(20\%\) 的数据,保证 \(n,m \le 16\)
  • 对于 \(40\%\) 的数据,保证 \(n,m \le 100\)
  • 对于 \(60\%\) 的数据,保证 \(n,m\le 500\)
  • 对于另 \(20\%\) 的数据,保证 \(n\times m \le 70000\)
  • 对于 \(100\%\) 的数据,保证 \(1\le n,m\le 3\times 10^5\)\(1\le n\times m\le 3\times 10^5\)
    ::::
    赛时打了个 \(40\) 分的暴力,因为没多少时间了,大概就是直接枚举钦定为 \(1\) 的位置,然后确定所有行的翻转情况并计算答案。
    枚举的位置数是 \(\mathcal{O}(nm)\) 的,每次计算答案也是 \(\mathcal{O}(nm)\) 的,总复杂度为 \(\mathcal{O}(n^2 m^2)\)
//40pts
#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace Cosmic{
	const int N=1000;
	int n,m,ans,a[N][N],b[N],cnt[N];
	inline void fre(){freopen("matrix.in","r",stdin);freopen("matrix.out","w",stdout);}
	inline void solve(){
		cin>>n>>m;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++){
				char ch; cin>>ch;
				a[i][j]=ch-'0';
				if(a[i][j]==1) cnt[j]++;
			}
		for(int i=1;i<=m;i++) if(cnt[i]==1) ans++;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++){
				if(a[i][j]==0){
					int p=0;
					for(int k=1;k<=m;k++){
						if(a[i][k]==0) a[i][k]=1,cnt[k]++;
						else a[i][k]=0,cnt[k]--;
					}//翻转第i行
					if(cnt[j]>1){
						for(int k=1;k<=n;k++)
							if(a[k][j]==1&&k!=i){
								b[++p]=k;
								for(int c=1;c<=m;c++){
									if(a[k][c]==0) a[k][c]=1,cnt[c]++;
									else a[k][c]=0,cnt[c]--;
								}
								if(cnt[j]==1) break;
							}//找到第j个位置是1的行并翻转 
					}
					int sum=0;
					for(int k=1;k<=m;k++) if(cnt[k]==1) sum++;
//					cout<<i<<' '<<j<<' '<<sum<<'\n';
					ans=max(ans,sum);
					for(int k=1;k<=m;k++){
						if(a[i][k]==0) a[i][k]=1,cnt[k]++;
						else a[i][k]=0,cnt[k]--;
					}
					for(int k=1;k<=p;k++)
						for(int c=1;c<=m;c++){
							if(a[b[k]][c]==0) a[b[k]][c]=1,cnt[c]++;
							else a[b[k]][c]=0,cnt[c]--;
						}//复原 
//					for(int i=1;i<=m;i++) cout<<cnt[i]<<' ';
				}
			}
		cout<<ans;
	}
}using namespace Cosmic;
signed main(){
	fre();
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int T=1;
	while(T--) solve();
}

正解肥肠纽币。考虑到 \(1\le n\times m\le 3\times 10^5\)。所以正解肯定要从 \(n \times m\) 下手。

考虑如果 \((i, j)\)\((k, l)\) 两个位置 \((j \neq l)\) 能够同时为 \(1\),那么两个位置所对应的行的翻转情况一定是一样的!

于是考虑对每个位置所对应的行的翻转情况进行 hash,一种行的翻转情况对应 hash 值计算方式如下:\(\sum^n_{i=1}C_ip^i\),其中 \(C_i\) 表示第 \(i\) 行是否翻转,\(p\) 为任意取的一个大质数。将所有的 \(hash\) 值丢进一个 \(map\) 里,相同值个数最多的就是答案。

时间复杂度 \(\mathcal{O}(nm)\)

#include<bits/stdc++.h>
#define int unsigned long long
using namespace std;
namespace Cosmic{
	const int N=3e5+10,mod=911;
	inline void fre(){freopen("matrix.in","r",stdin);freopen("matrix.out","w",stdout);}
	int n,m,pw[N],_[N],__[N];
	map<int,int> cnt;
	vector<int> a[N];
	inline void init(){
		pw[0]=1;
		for(int i=1;i<=N;i++) pw[i]=pw[i-1]*mod;
	}
	inline void solve(){
		cin>>n>>m;
		for(int i=1;i<=n;i++){
			a[i].push_back(0);
			for(int j=1;j<=m;j++){
				char ch; cin>>ch;
				a[i].push_back(ch-'0');
			}
		}
		int mx=0;
		for(int j=1;j<=m;j++){
			for(int i=1;i<=n;i++) _[i]=_[i-1]+a[i][j]*pw[i];
	        __[n+1]=0;
	        for(int i=n;i>=1;i--) __[i]=__[i+1]+a[i][j]*pw[i];
	        for(int i=1;i<=n;i++){
	            int val=_[i-1]+__[i+1]+(1-a[i][j])*pw[i];
	            cnt[val]++;
	            if(cnt[val]>mx) mx=cnt[val];
	        }
		}
		cout<<mx;
	}
}using namespace Cosmic;
signed main(){
	init();
	fre();
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int T=1;
	while(T--) solve();
}

9.3

T1:CF1732A(无多测版)

手玩发现答案总 \(\le 3\),分类讨论即可

#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace Cosmic{
	inline void fre(){freopen("unite.in","r",stdin);freopen("unite.out","w",stdout);}
	const int N=1e5+10; 
	int n,a[N],s; 
	inline void solve(){
		cin>>n;
		for(int i=1;i<=n;i++) {
			cin>>a[i];
			if(i==1) s=a[i];
			else s=__gcd(s,a[i]);
		}
		if(s==1){
			cout<<0;
			exit(0);
		}
		if(n==1){
	        if(a[1]==1) cout<<0<<'\n';
	        else cout<<1<<'\n';
	        exit(0);
	    }
	    if(n==2){
	        if (__gcd(a[1],a[2])==1) cout<<0<<'\n';
	        else if (__gcd(a[1],__gcd(a[2],2ll))==1) cout<<1<<'\n';
	        else cout<<2<<'\n';
	        exit(0);
	    }
	    int gcd=0;
	    for (int i=1;i<=n-2;i++) gcd=__gcd(a[i],gcd);
	    if (__gcd(gcd,__gcd(a[n],a[n-1]))==1) cout<<0<<'\n';
	    else if (__gcd(gcd,__gcd(__gcd(a[n],n),a[n-1]))==1) cout<<1<<'\n';
	    else if (__gcd(gcd,__gcd(a[n],__gcd(a[n-1],n-1)))==1) cout<<2<<'\n';
	    else cout<<3<<'\n';
	}
}using namespace Cosmic;
signed main(){
	fre();
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int T=1;
	while(T--) solve();
}

T2:CF902B

赛时打了个 dfs,赛后一看,RE 了,结果发现正解是如果当前结点 \(u\) 和自己的父亲结点 \(p_u\) 颜色不一样,操作次数就加一。。。无语了。

#include<bits/stdc++.h>
//#define int long long
using namespace std;
namespace Cosmic{
	inline void fre(){freopen("color.in","r",stdin);freopen("color.out","w",stdout);}
	const int N=1e5+10;
	int n,fa[N],c[N],ans;
	inline void solve(){
		cin>>n;
		for(int i=2;i<=n;i++) cin>>fa[i];
		for(int i=1;i<=n;i++) cin>>c[i];
		for(int i=1;i<=n;i++) if(c[i]!=c[fa[i]]) ans++;
		cout<<ans;
	}
}using namespace Cosmic;
signed main(){
	fre();
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T=1;
	while(T--) solve();
}

T3:CF1822G2(无多测版)

最简单的就是 \(\mathcal{O}(n^3)\) 的暴力(赛时机房里有人此题保龄,被ll训了)。

然后看到有 \(a_i \le 5 \times 10^3\)\(a_i \le 10^6\) 的部分分。考虑优化,使复杂度带一个值域。

考虑枚举 \(a_i\),对于任意一个满足条件的正整数 \(b\) 的大小一定是 \(\le \sqrt{V}\) 的(这里 \(V\) 表示值域)

然后判断 \(a_i \times b\)\(a_i \times b^2\) 有没有即可。

#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace Cosmic{
	inline void fre(){freopen("triple.in","r",stdin);freopen("triple.out","w",stdout);}
	const int N=1e5+10;
	int n,a[N],mx,ans;
	map<int,int> mp;
	inline void solve(){
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			mx=max(mx,a[i]);
			mp[a[i]]++;
		}
		stable_sort(a+1,a+n+1);
		int m=unique(a+1,a+n+1)-a-1;
		for(int i=1;i<=m;i++) ans+=mp[a[i]]*(mp[a[i]]-1)*(mp[a[i]]-2);
		for(int i=1;i<=m;i++)
			for(int b=2;a[i]*b*b<=mx;b++){
				int x=a[i],y=a[i]*b,z=a[i]*b*b;
				if(mp[x]&&mp[y]&&mp[z]) ans+=mp[x]*mp[y]*mp[z];
			}
		cout<<ans;
	}
}using namespace Cosmic;
signed main(){
	fre();
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T=1;
	while(T--) solve();
}

但是这个代码有一些特例的点是卡不过去的,正解是根号分治,复杂度为 \(\mathcal{O}(nV^{\frac{1}{3}})\)

但但但但但是,我们动用人类智慧。使用手写 hash 或者是在代码前面加一个火车头,就卡过去了。本人在网上随便找了篇火车头优化,加在上面的代码就卡过去了(9.3日 13:45 时发现只要机子没被宇宙射线攻击,\(\mathcal{O}(n\sqrt{V})\)也能过,最大点的时间为 \(1.5s\))。

T4:CF1827B2

题目大意就是:
有一个元素两两不同的长度为 \(n\) 的序列 \(A\),但是这个序列可能是无序的。

\(f_{l,r}\) 表示在只考虑序列 \(A\) 的区间 \([l,r]\) 的前提下,让子序列 \([A_l,A_{l+1},...,A_r]\) 有序的最小代价和。

\[\sum_{i=1}^n \sum_{j=i}^n f_{i,j} \]

的值。

每次操作完后加代价算感觉很神秘不会码,考虑算出对所有的子序列都做一遍操作的总代价以后,减去多余操作的部分。

注意到问题本质转化为了有多少个三元组 \((i, j, k)(i \le j < k)\) 满足区间 \([i, j]\) 的最大值小于 \([j+1, k]\) 的最小值。

不如考虑枚举区间 \([i, j]\) 的最大值 \(x\),可以通过 set 找到前一个比 \(x\) 大的数的位置 \(a\),后一个比 \(x\) 大的数的位置 \(c\)\(c\) 后面第一个比 \(x\) 小的数的位置 \(d\)

那么 \(i\) 可以在 \((a, b]\) 中选,\(j\) 一定是 \(c-1\)\(k\) 可以在 \([c,d]\) 中选,贡献为 \((b-a) \times (d-c)\)

时间复杂度 \(\mathcal{O}(n log n)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace Cosmic{
	inline void fre(){freopen("sort.in","r",stdin);freopen("sort.out","w",stdout);}
	const int N=3e5+10;
	int n,a[N],ans,pos[N];
	set<int> _,__;
	inline bool cmp(int x,int y){
		return a[x]<a[y];
	}
	inline void solve(){
		cin>>n;
		for(int i=1;i<=n;i++) cin>>a[i];
		for(int i=1;i<=n;i++) ans=ans+(i-1)*(n-i+1);
//		cout<<ans<<'\n';
		for(int i=0;i<=n+1;i++) _.insert(i);
		__.insert(0); __.insert(n+1);
		for(int i=1;i<=n;i++)pos[i]=i;
		sort(pos+1,pos+n+1,cmp);
//		for(int i=1;i<=n;i++) cout<<pos[i]<<' ';
//		putchar('\n');
		for(int i=1;i<=n;i++){
			int y=pos[i];
			int x=*(--_.lower_bound(y)),z=*_.upper_bound(y);
			if(z!=n+1){
				int cw=*__.upper_bound(z);
				ans-=(y-x)*(cw-z);
			}
			_.erase(y); __.insert(y);
		}
		cout<<ans;
	}
}using namespace Cosmic;
signed main(){
	fre();
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T=1;
	while(T--) solve();
}
posted on 2026-09-03 20:21  yzflower  阅读(5)  评论(0)    收藏  举报