发现已经错过最美的花期|

ricky_lin

园龄:3年8个月粉丝:11关注:2

【题解】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 @   ricky_lin  阅读(14)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 有我 周深
有我 - 周深
00:00 / 00:00
An audio error has occurred.

作词 : 唐恬/闫光宇

作曲 : 钱雷

编曲 : 赵兆/付虹宇

制作人 : 赵兆

出品 : 共青团中央宣传部

版权 : 中国青少年新媒体协会

制作单位 : 能量悦动音乐

发行单位 : 银河方舟StarNation

出品人 : 郭峰

总监制 : 汤杰

总策划 : 钟亚楠

总统筹 : 金慧子

音乐监制 : 李天鹏/李三木

制作执行 : 张不贰/高聪怡

项目宣发 : 肖健/张国党/孙小千/戴胤/孙雯璟

音乐推广 : 代诗琪/杜思潮/马越/程铁峰/傅之豪

钢琴 : 赵兆

吉他 : 伍凌枫

贝斯 : 韩阳

鼓 : 武勇恒

合唱设计 : 赵兆

合唱 : 凡尔赛合唱团

人声录音 : 耿潇微

人声录音室 : 55TEC Studio Beijing

配唱 : 徐威@52Hz Studio (Shanghai)

混音 : 李游(小骷髅)@55TEC Studio Beijing

海报 : 格子

特别鸣谢 : 周深工作室

世界问 你是谁 来自哪 请回答

爱什么 梦什么 去何方 请回答

答案有 一百年的时光

我来自 硝烟中 课桌旁 的太阳

我来自 硝烟中 课桌旁 的太阳

他和她 宣的誓 迎的仗

来自那 燃烧的 和我一样 的年华

来自世间 一对平凡的夫妻 身旁

来自世间 一对平凡的夫妻 身旁

来自昨天 谁以青春赴万丈 理想

我是寸土 不让的 家乡啊

我是绝不 低头的 倔强啊

接过万千热血 的初衷

当有对答世界 的音量

要怎么形容明天 像我一样

要怎么形容明天 像我一样

承风骨亦有锋芒 有梦则刚

去何方 去最高 的想象

前往皓月星辰 初心不忘

那未来如何登场 有我担当

那未来如何登场 有我担当

定是你只能叫好 那种辉光

护身旁 战远方 有我啊

我的名字就是 站立的地方

Wu~

我的样子 就是 明天的模样

我是朝阳 落在乡间听书声 朗朗

我是朝阳 落在乡间听书声 朗朗

我是屏障 为谁挡一程厄运 的墙

我要一生 清澈地 爱着啊

我要长歌 领着风 踏着浪

朝着星辰大海 的方向

当有对答世界 的音量

要怎么形容明天 像我一样

要怎么形容明天 像我一样

承风骨亦有锋芒 有梦则刚

去远方 去最高 的想象

前往皓月星辰 初心不忘

那未来如何登场 有我担当

那未来如何登场 有我担当

定是你只能叫好 那种辉光

护身旁 战远方 有我啊

一生骄傲为我 站立的地方

Wu~

我的样子 就是 中国的模样

Wu~~~ Wu~~~

当炬火 去化作那道光

“谨以此歌献给一代代不负时代重托的中国青年”

“谨以此歌献给一代代不负时代重托的中国青年”