题单
我单推这些题(
两种做法应该都是很好的方法。
时间复杂度 O(nlog2n)。
二分答案后赋权值为 0/1,有很优美的性质。
二分答案 mid,我们把原排列中大于等于 mid 的数都标记为 1,小于 mid 的都标记为 0。然后对于每个操作我们就将 01 序列排个序。最后如果第 p 个位子仍是 1 的话就是可行的。
太懒了并没有写
时间复杂度 O(nlogn)。
考虑 ODT 的维护,可以把区间排序操作类比为区间染色。
然后对于区间分裂合并直接开权值线段树分裂合并即可。
时间复杂度证明即线段树分裂合并复杂度(
很好写 这是好的(
const int N=4e5+5,logN=20;
int n,m,rt[N],nw=1,a[N];
struct range{
int l,r,v;
range(int L,int R=-1,int V=0):l(L),r(R),v(V){}
bool operator < (range x) const{return l<x.l;}
};
set<range> qaq;
#define IT set<range>::iterator
struct SGT{
#define mid ((l+r)>>1)
#define xl (ls[x])
#define xr (rs[x])
int sum[N*logN],ls[N*logN],rs[N*logN],st[N],tp,cnt;
inline int newnode(){
if(tp) return st[tp--];
return ++cnt;
}
inline void pushup(int x){sum[x]=sum[xl]+sum[xr];}
inline void del(int x){sum[x]=xl=xr=0;st[++tp]=x;}
inline int insert(int x,int l,int r,int k,int v){
if(!x) x=newnode();
if(l==r){sum[x]+=v;return x;}
if(k<=mid) xl=insert(xl,l,mid,k,v);
else xr=insert(xr,mid+1,r,k,v);
pushup(x);return x;
}
inline int merge(int x,int y,int l,int r){
if(!x||!y) return x|y;
if(l==r){sum[x]+=sum[y];del(y);return x;}
xl=merge(xl,ls[y],l,mid);xr=merge(xr,rs[y],mid+1,r);
pushup(x);return x;
}
inline void split(int x,int &y,int l,int r,int k){
if(!x) return ;y=newnode();
if(k>sum[xl]) split(xr,rs[y],mid+1,r,k-sum[xl]);
else xr^=rs[y]^=xr^=rs[y];
if(k<sum[xl]) split(xl,ls[y],l,mid,k);
pushup(x);pushup(y);return ;
}
inline int kth(int x,int l,int r,int k){
if(l==r) return l;
if(sum[xl]>=k) return kth(xl,l,mid,k);
return kth(xr,mid+1,r,k-sum[xl]);
}
inline int query(int x,int l,int r,int L,int R){
if(L<=l&&r<=R) return sum[x];
ll res=0;
if(mid>=L) res+=query(xl,l,mid,L,R);
if(mid<R) res+=query(xr,mid+1,r,L,R);
return res;
}
}T;
inline IT split(int pos){
IT tmp=qaq.lower_bound(range(pos));
if(tmp!=qaq.end()&&tmp->l==pos) return tmp;
--tmp;int l=tmp->l,r=tmp->r,v=tmp->v;
if(!v) T.split(rt[l],rt[pos],1,n,pos-l);
else T.split(rt[l],rt[pos],1,n,r-pos+1),rt[l]^=rt[pos]^=rt[l]^=rt[pos];
qaq.erase(tmp);qaq.insert(range(l,pos-1,v));
return qaq.insert(range(pos,r,v)).first;
}
inline void assign(int l,int r,int op){
IT R=split(r+1),L=split(l),nw=L;++nw;
while(nw!=R) T.merge(rt[l],rt[nw->l],1,n),++nw;
qaq.erase(L,R);qaq.insert(range(l,r,op));
}
int main(){
rd(n);rd(m);
for(re i=1;i<=n;++i){
rd(a[i]);rt[i]=T.insert(rt[i],1,n,a[i],1);qaq.insert(range(i,i,0));
}
qaq.insert(range(n+1,n+1,0));
for(re i=1;i<=m;++i){
int op,l,r;rd(op);rd(l);rd(r);
assign(l,r,op);
}
rd(m);
for(IT nw=qaq.begin();nw!=qaq.end();++nw){
if(nw->r-nw->l+1<m) m-=nw->r-nw->l+1;
else{
wr(T.kth(rt[nw->l],1,n,nw->v?nw->r-nw->l+2-m:m));puts("");break;
}
}
return 0;
}
很好的贪心题。
令 A 图连通块个数不小于 B 图。
我们证明 B 图最后连通块个数为 1。
若 A 连通块数量大于 1,若此时无法操作,考虑到 A 图中连通块 a, b。
对于 a,b 中的点,他们在 B 图中一定连通。
那么考虑到 A 中所有连通块,可发现 B 全连通,即连通块数量为 1。
由此贪心分析可知,随意加可行边即可。
因此 D1 就可以直接暴力枚举点对加边即可,时间复杂度 O(n2α(n))。
优化贪心。
考虑一个中心点 s。
我们先让所有点与 s 尝试连边。
然后连完后令 A 图中与 s 不连通的点集为 L,B 图中与 s 不连通的点集为 R。
显然 L∩R=∅。
考虑 l∈L 和 r∈R。
由定义有 A 图中 l 与 s 不连通,r 与 s 连通,B 图相反。
那么任意 l 与 r 都可连边。
然后只要随意配对完 L 或 R 就行了,此时一幅图变成一个连通块。
时间复杂度 O(nα(n))。
很好的构造题。
观察到有 29<n<210。
大概一看就是考虑二进制分组了。
1∼n 标号不太方便,我们改成 0∼n−1
先看 maxk=30 的限制。
我们先选 10 只鼠编号为 0∼9,让它们分别喝对应的水,编号为 i 的鼠喝的水的编号转化为二进制 2i 这一项系数为 1。
考虑到变异鼠,我们每个鼠复制一下成三个鼠,这样就能辨别出变异鼠了。
然后考虑去掉变异鼠每个鼠存活情况,编号为 i 的鼠存活当且仅当毒水编号转化为二进制后 2i 这一项系数为 0。
然后算出来即可。
然后考虑 maxk=15。
显然不是 maxk2 的关系,因为这没啥实际意义。
很容易想到 ⌈log2n⌉+⌈log2⌈log2n⌉⌉=14,盲猜是 ⌈log2n⌉+⌈log2⌈log2n⌉⌉+1=15。
⌈log2⌈log2n⌉⌉ 这个东西容易想到是对上面我们鼠的编号再二进制分组。
考虑一些水的集合 S 和一些鼠的集合 M,保证对于 ∀s∈S 水 s 被 M 中偶数个鼠喝了,且对于 ∀m∈M 鼠 m 喝的水都在 S 中。
有结论若 M 中死了偶数个则无变异鼠,若 M 中死了奇数个则有变异鼠。由定义和恰有一瓶毒水一只变异鼠易证。
那么考虑对 0∼9 这些鼠再进行类似地二进制分组,编号为 10+i 的鼠喝的水为编号转化为二进制后 2i 系数为 1 的鼠喝的水的异或。(异或有很好的性质能保证每瓶水被喝偶数次。)
为防止这 10∼13 四只鼠里有内鬼,再拿一只鼠喝他们喝的水的异或即可。
然后先考虑 10∼14 这里有无内鬼,若有则表明 0∼9 里无内鬼,直接用上面 maxk=30 的方法算即可。
若 10∼14 无内鬼,则考虑 10∼13 和它们各自支配的鼠,分别考虑是否有内鬼,若 10+i 和支配的鼠里有内鬼,则表明内鬼鼠编号转化为二进制后 2i 系数为 1。把内鬼算出来然后直接把它存活状态取反即可。然后还像上面一样算出毒水即可。
记得 n=1/2 拿出来特判。
记得 cout
不知道为啥之前不用 cout
基本全 T 了。
先咕
时光倒流 和那道 JOI 的断层思想差不多。
考虑倒推,即从最后一个人开始向前进行。
我们考虑 (i,j) 最后都没被扔掉,则在前面某人为 (x,i) 时,我们扔掉的一定是 x。
那么此时我们就需要保证再往前 x 不能被扔掉。
若前面出现 (x,y) 且都不能被扔掉,那么 (i,j) 一定不是一组解。
注意到这里每次拓展实际是一个的拓展,而非一对的拓展。
我们可以将枚举 (i,j) 换成枚举 i,记录下如果要留下 i 前面一定不能被扔掉的数集 Si。
此时若 Si∩Sj=∅,则 (i,j) 满足题意。模型意义下即表示 i 和 j 前面不能被扔掉的东西不重复,因为在 Si 中所有除 i 的东西都会被扔掉,而东西不能重复扔掉。
考虑到 Si 和 Sj 实际上只要对位完成与运算,用 bitset 存储即可。
时间复杂度 O(nm+n3ω)。
贪心合并 证伪真的挺难(
一开始有个很简单的贪心想法,就是每次取行列中总和最大的那个。
然后你冲了一发,发现你挂了。
WA on #4。
然后其实你发现行列是对互相有后效影响的。
比如当你最大值又有行又有列,你是选哪个呢?
那么考虑到行或者列自己是不影响的。
那么考虑行列分开贪心,贪心策略同上。
令 li 为行选了 i 个最大答案,ri 为列。
显然有 ans=maxki=0{li+ri−i×(k−i)×p},这时你把行列拆开就可以直接算出行列之间相互影响,保证了贪心的正确性。
记得开 long long。
贪心随便拿个东西存一下就行了,我整了个 multiset,时间复杂度 O(klogmax(n,m))。
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 三行代码完成国际化适配,妙~啊~
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?