【题解】CF1718 合集

CF1718A Burenka and Traditions

标签:思维题 \(C\)

我们显然可以发现我们只用考虑进行区间长度为 \(1\) 或者 \(2\) 的操作(其他情况下,可以由长度为 \(1\)\(2\) 的区间等价拼得)

我们又发现,正常情况下显然操作次数为 \(n\),那什么时候才能减少操作次数呢?

  • 我们发现,一个长为 \(k\) 的异或和为 \(0\) 的区间显然只需要 \(n-1\) 个长度为 \(2\) 的区间解决,代价是 \(n-1\) 的。

然后我们就可以每次找到极小的异或和为 \(0\) 的区间的个数,最后答案即为 \(n-k\)

备注:"极小" 意为不和其他的异或和为 \(0\) 的区间有交集

如何找这些区间呢,异或和为 \(0\) 区间 \([l,r]\) 显然有一个性质:\(l-1\) 的前缀和 和 \(r\) 的前缀和相等。

基于这个性质,我们可以在 map/set 里面存放所有的前缀和,如果前缀和在之前出现过那么就有一个这样的区间,每次成功匹配后将 map/set 清空即可(为了满足极小)。

code:

#include<bits/stdc++.h>
using namespace std;
const int NN = 1e5 + 8;
int T,n;
int a[NN];
int pre[NN];

void solve(){
	set<int> s;
	scanf("%d",&n);
	for(int i = 1; i <= n; ++i) scanf("%d",&a[i]);
	s.insert(0);
	int ans = n;
	for(int i = 1; i <= n; ++i){
		pre[i] = pre[i-1] ^ a[i];
		if(s.count(pre[i])){
			--ans;
			s.clear();
			pre[i] = 0;
		}
		s.insert(pre[i]);
	}
	printf("%d\n",ans);
}

int main(){
	scanf("%d",&T);
	while(T--){
		solve();
	}
}

CF1718B Fibonacci Strings

标签:思维题 \(C\)

我们首先满足输出 YES 的第一个要求显然是 \(\sum c_i\)fibonacci 序列的前缀和之一。

因为我们需要满足一个数列中的任意数不能删连续的斐波那契数,所以一个数的 fibonacci 的分解显然是唯一的。

我们考虑一个数的 fibonacci 分解显然是斐波那契数列从大到小,能减就减。

对于一堆数的分解我们显然是让当前数列最大的数减去最大的斐波那契数。


所以我们从大到小枚举可用的 fibonacci 数,然后进行上面的操作即可,对于查找最大数,显然可以使用优先队列。

我们会出现下面两种异常情况:

  • 如果说当前数列最大的数小于当前的斐波那契数,那么显然就是无解。

  • 如果当前数列最大的数被上一个斐波那契数减过,那么就去选第二大的(在代码实现的时候可以将一次操作后的数延后压入优先队列)。

code:

#include<bits/stdc++.h>
using namespace std;
const int NN = 1e2 + 8;
typedef long long ll;

int T,n;
ll f[NN],pre[NN];
ll a[NN];
ll p[NN];//p 表示 第 i 个斐波那契数 减去后剩下的数

void solve(){
	scanf("%d",&n);
	ll sum = 0;
	priority_queue<ll> Q;
	for(int i = 1; i <= n; ++i) scanf("%lld",&a[i]),sum += a[i],Q.push(a[i]);
	
	int lim = 0;//可用的 fibonacci 数列的上界
	for(int i = 1; i <= 70; ++i){
		if(sum == pre[i]){
			lim = i;break;
		}
	}
	if(!lim) return puts("NO"),void(0);//没有 `fibonacci` 序列的前缀和 和 数列的和对应
	for(int i = lim; i >= 1; --i){
		if(Q.empty()) return puts("NO"),void(0);
		ll x = Q.top();Q.pop();
		if(x < f[i]) return puts("NO"),void(0);
		x -= f[i];
		p[i] = x;
		if(i <= lim - 1){//延后压入优先队列
			if(p[i+1]) Q.push(p[i+1]);
		}
	}
	puts("YES");
}
int main(){
	f[1] = f[2] = 1;
	pre[1] = 1;pre[2] = 2;
	for(int i = 3; i <= 70; ++i){//预处理 fibonacci & 前缀和
		f[i] = f[i-1] + f[i-2];
		pre[i] = pre[i-1] + f[i];
	}
	scanf("%d",&T);
	while(T--){
		solve();
	}
}

CF1718C Tonya and Burenka-179

标签:思维题 \(B^-\)

根据某经典结论,我们从同一节点开始的循环节长度相同的序列构成的元素相同,且循环节一定是序列长度的质因字

所以我们只需要对于每一个长度为质因子的循环节枚举所有的节点。

然后对于单点修改,我们只需要对于经过该点的 每一个循环节长度为质因子的序列进行更改即可。

至于如何维护最大值,使用 set 即可。

时间复杂度:\(O(n\omega(n)\log n)\)

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int NN = 3e5 + 8;
ll a[NN];
ll f[30][NN];
int cnt;
int n,q;
ll g[NN];

multiset<ll> s; 

void init(){
	s.clear();
	int m = n;cnt = 0;
	g[++cnt] = 1;
	for(int i = 2; i <= m; ++i){
		if(m % i == 0){
			g[++cnt] = n / i;
			while(m % i == 0) m /= i;
		}
	}
	if(m >= 2883)cout << m << '\n';
	if(g[cnt] == n) --cnt;
	
	for(int i = 1; i <= cnt; ++i){
		for(int j = 1; j <= g[i]; ++j){
			ll res = 0;
			for(int k = j; k <= n; k += g[i])
				res += a[k];
//			fprintf(stderr,"%d %d %d\n",res,g[i],j);
			s.insert(res * g[i]);
			f[i][j] = res;
		} 
	}
}
void modify(int x,ll num){
	for(int i = 1; i <= cnt; ++i){
		int j = (x-1)%g[i]+1;
		s.erase(s.find(f[i][j] * g[i]));
		f[i][j] = f[i][j] - a[x] + num;
		s.insert(f[i][j] * g[i]);
	}
	a[x] = num;
}

void solve(){
	cin >> n >> q;
	for(int i = 1; i <= n; ++i) cin >> a[i];
	init();
	cout << *prev(s.end()) << '\n';
	for(ll i = 1,p,x; i <= q; ++i){
		cin >> p >> x;
		modify(p,x);
		cout << *prev(s.end()) << '\n';
	}
}

int main(){
//	freopen("1.out","w",stdout);
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int T;
	cin >> T;
	while(T--){
		solve();
	}
} 

CF1718D Permutation for Burenka

标签:DS \(B\) | 思维题 \(B\)

这道题的要求本质上是让两个序列构建出的 笛卡尔树 同构

我们可以先按照原序列构建 笛卡尔树,然后将给出的序列的非零数字填上去。

对于一个为 \(0\) 的节点填入的数的限制显然是:\([\)儿子节点的最小值\(,\)父亲节点的最大值\(]\)

然后我们的问题就变成了将一些数和一些区间能否一一对应并满足数包含在这个区间内。

我们显然可以从小到大枚举数,并每次找到 被未对应的 包含它的 区间中右端点最小的区间,最后剩下的区间的左端点,就是最后一个数的最小值,求最大值同理。

当然本代码中是用区间去对应数,本质相同,相关证明可以参照 \(Alex\_Wei\) 的洛谷题解。

code:

#include<bits/stdc++.h>
using namespace std;
const int NN = 3e5 + 8;
int n,q;
int p[NN],a[NN],r[NN];
int flag;

struct Interval{
	int l,r;
};

vector<Interval> Lst; 

namespace Cartesian_Tree{
	struct Tree{
		int l,r,val;
		int son[2];
		int famin,sonmax;
		#define l(x) tree[x].l
		#define r(x) tree[x].r
		#define val(x) tree[x].val
		#define ls(x) tree[x].son[0]
		#define rs(x) tree[x].son[1]
		#define famin(x) tree[x].famin
		#define sonmax(x) tree[x].sonmax
	}tree[NN];
	int rt;
	int sta[NN],top;
	void build(){
		for(int i = 1; i <= n; ++i){
			while(top && p[sta[top]] <= p[i]) ls(i) = sta[top],--top;
			sta[++top] = i;
			if(top == 1) rt = sta[top];
			else rs(sta[top-1]) = sta[top];
		}
		top = 0;
	}
	void dfs(int x,int l,int r,int num){
		if(l > r) return; 
		l(x) = l;r(x) = r;
		famin(x) = num;
		if(l == r) return;
		if(val(x) != 0) num = min(num,val(x));
		dfs(ls(x),l,x-1,num);
		dfs(rs(x),x+1,r,num);
		sonmax(x) = max(max(sonmax(ls(x)),sonmax(rs(x))), max(val(ls(x)),val(rs(x))));
		if(val(x) && (val(x) < sonmax(x) || val(x) > famin(x))) flag = 1;
	}
}
using namespace Cartesian_Tree;
	
void solve(){
	Lst.resize(0);
	flag = 0;
	scanf("%d%d",&n,&q);
	for(int i = 1; i <= n; ++i) scanf("%d",&p[i]),l(i) = r(i) = ls(i) = rs(i) = val(i) = sonmax(i) = famin(i) = 0;
	int k = 0;
	for(int i = 1; i <= n; ++i) scanf("%d",&a[i]),k += (a[i] == 0);
	build();
	set<int> s;
	for(int i = 1; i <= n; ++i) val(i) = a[i];
	for(int i = 1,x; i < k; ++i) scanf("%d",&r[i]),s.insert(r[i]);
	dfs(rt,1,n,0x3f3f3f3f);
	
	for(int i = 1; i <= n; ++i) if(!val(i)) Lst.push_back({sonmax(i),famin(i)});
	int L = 0,R = 0x3f3f3f3f;
	
	sort(Lst.begin(),Lst.end(),[&](Interval x,Interval y){return x.l > y.l;});
	for(auto i : Lst){
		auto x = s.upper_bound(i.r);
		if(x == s.begin() || *prev(x) < i.l){
			if(L){flag = 1;break;}
			L = i.l;
		}
		else s.erase(prev(x));
	}
	
	s.clear();
	for(int i = 1,x; i < k; ++i) s.insert(r[i]);
	sort(Lst.begin(),Lst.end(),[&](Interval x,Interval y){return x.r < y.r;});
	for(auto i : Lst){
		auto x = s.lower_bound(i.l);
		if(x == s.end() || *x > i.r){
			if(R != 0x3f3f3f3f){flag = 1;break;}
			R = i.r;
		}
		else s.erase(x);
	}
	
	for(int i = 1,x; i <= q; ++i){
		scanf("%d",&x);
		if(!flag && L <= x && x <= R) puts("YES");
		else puts("NO");
	}
}

int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		solve();
	} 
}

CF1718E Impressionism

标签:杂项 \(B^+\)

考虑参考的题解第一篇做法。

简单来说就是我们现将每一行按所有元素和以及其元素位置(这里的元素位置指的是列的状态)进行 Hash,然后对列同理。

然后我们先确定每一行的位置,如果我们的行固定了的话,显然交换列的方式也是好求的,我们这样就做完了。

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int NN = 2e5 + 8;
const ull K = 374653646846437ull;

int n,m;

inline int read(){
	register char c = getchar();
	register int res = 0;
	while(!isdigit(c)) c = getchar();
	while(isdigit(c)) res = res * 10 + c - '0',c = getchar();
	return res;
}

struct Num{
	int pos,num;
};

struct Matrix{
	ull hR[NN],hC[NN];
	vector<Num> R[NN],C[NN];
	vector<int> a[NN];
	
	void init(){
		for(int i = 0; i < n; ++i){
			a[i].resize(0);
			for(int j = 0,x; j < m; ++j){
				x = read();
				a[i].push_back(x);
				if(x != 0) R[i].push_back({j,x}),C[j].push_back({i,x});
			}
		}
		
		for(int i = 0; i < n; ++i) sort(R[i].begin(),R[i].end(),[&](Num a,Num b){return a.num < b.num;});
		for(int i = 0; i < m; ++i) sort(C[i].begin(),C[i].end(),[&](Num a,Num b){return a.num < b.num;});
		
		for(int i = 0; i < n; ++i)
			for(auto j : R[i])
				hR[i] = hR[i] * K + j.num;
		for(int i = 0; i < m; ++i)
			for(auto j : C[i])
				hC[i] = hC[i] * K + j.num;
		
		for(int t = 0; t < min(n, m); ++t) {//
			for(int i = 0; i < n; ++i)
				for(auto j : R[i])
					hR[i] = hR[i] * K + hC[j.pos];
			for(int j = 0; j < m; ++j)
				for(auto i : C[j])
					hC[j] = hC[j] * K + hR[i.pos];
	    }
	}
}A,B;

vector<array<int, 3> > ans;

vector<int> get(ull a[], ull b[], int n, int id) {
	unordered_multimap<ull, int> B;
	
	for(int i = 0; i < n; ++i) B.emplace(b[i], i);
	
	vector<int> P(n), iP(n);
	for(int i = 0; i < n; ++i) {
		auto it = B.find(a[i]);
		if(it == B.end()) {
			puts("-1");
			exit(0);
		}
		P[i] = it->second;
		B.erase(it);
	}
	
	for(int i = 0; i < n; ++i) iP[P[i]] = i;
	for(int i = 0; i < n; ++i)
		while(P[i] != i){
			ans.push_back({id, i, P[i]});
			swap(P[i], P[P[i]]);
		}
//	for(auto i : iP) printf("%d ",i);
//	puts("");
	return iP;
}

int main(){
	n = read();m = read();
	A.init();B.init();
	
	fill_n(A.hC,NN,0);fill_n(B.hC,NN,0);
	
	for(auto i : get(A.hR,B.hR,n,1))
		for(int j = 0; j < m; ++j)
			A.hC[j] = A.hC[j] * K + A.a[i][j];
	for(int i = 0; i < n; ++i)
		for(int j = 0; j < m; ++j)
			B.hC[j] = B.hC[j] * K + B.a[i][j];
	
	get(A.hC,B.hC,m,2);
	printf("%d\n", int(ans.size()));
	for(auto a : ans)
		printf("%d %d %d\n", a[0], a[1] + 1, a[2] + 1);
}
posted @ 2023-10-25 21:08  ricky_lin  阅读(8)  评论(0编辑  收藏  举报