11.8~11.11 模拟赛

11.11 %你赛

t1

原题

给定一个长度为 \(M\) 的正整数序列 \(X_1, X_2, \ldots, X_M\),我们希望将这个序列变为升序。一个序列是升序的当且仅当对所有 \(i\)\(1 \leq i \leq M-1\))满足 \(X_i \leq X_{i+1}\)

为了将序列 \(X\) 变为升序,可以进行如下操作任意次数(包括0次):

  • 对于某个 \(i\)\(1 \leq i \leq M\)),将 \(X_i\) 乘以 2。

定义 \(f(X)\) 为通过最少的操作次数将 \(X\) 变为升序所需的操作次数。

现在,给定一个长度为 \(N\) 的正整数序列 \(A_1, A_2, \ldots, A_N\),以及 \(Q\) 个查询。每个查询给出两个整数 \(l\)\(r\)\(1 \leq l \leq r \leq N\))。查询的目标是计算 \(f(A_l, A_{l+1}, \ldots, A_r)\),即从 \(A\) 中截取 \(l\)\(r\) 的子序列升序所需的最小操作次数。

请计算并输出每个查询的答案。

题解

先考虑整个序列的答案。
对于每个 \(a _ { i } > a _ { i + 1 }\) 预处理出一个 \(c _ { i }\) 数组, 表标最小的 \(c _ { i }\) 使得: $a _ { i + 1 } \times 2 ^ { c _ { i } } \geq a _ { i } $。
我们发现这个 \(c _ { i }\) 是可以继承的,即对于某个(另一个) $a _ { j } > a _ { j + 1 } $, 在预处理出最小的 \(x\) 使得 \(a _ { j + 1 } \times 2 ^ { x } \geq a _ { j }\) 后 , \(c _ { j }\) 应该等于 $c _ { j - 1 } + x $。

我们还要考虑数处理出一个大小为负的 \(c _ { i + 1 }\) 用于抵消之前并不必要的影响。具体地,找到最大的 \(x\) 满足 $a _ { i } \times 2 ^ { x } \leq a _ { i + 1 } $, 则 $c _ { i } = - x $。 定义 \(s _ { i } = \max \left\{ 0 , s _ { i - 1 } + c _ { i } \right\} ,\) 则 $a n s = \sum _ { i = 1 } ^ { n } s _ { i } $。

考虑区间询问 \([ l , r ]\), 我们的答案是这样的:

\(\sum _ { i = l } ^ { r } \max \left\{ \left( \sum _ { j = l } ^ { i } c _ { j } \right) , 0 \right\}\)或$ \sum _ { i = l } ^ { r } \max \left( s _ { i } - s _ { l - 1 } , 0 \right)\sum _ { i = l } ^ { r } \max \left( s _ { i } - s _ { l - 1 } , 0 \right)$

先考虑如果没有这一层取mx的话应该怎么算,可以交换求和符号并结合前缀和进行化简:

\[\sum _ { i = l } ^ { r } \sum _ { j = l } ^ { i } c _ { j } = ( r + 1 ) \times \sum _ { i = l } ^ { r } c _ { i } - \sum _ { i = l } ^ { r } i \cdot c _ { i } \]

显然只要预处理出 \(c _ { i }\) 的前缀和与 \(i \cdot c _ { i }\) 的前缀和就能做到 \(O ( 1 )\) 求。
把非零段都提取出来,然后用上面说的方法快速计算。考虑预处理出对于所有的 \(i \in [ 1 , n ]\) 右边第一个会求和中断的位置 $p _ { i } $, 这样就能从有值的位置跳到距离最近的断点(最后一个前缀和不为0的位置), 然后对每段答案求和。

考虑什么时候会出现 $s _ { i } < 0 $, 一定是前一个数比后一个数大了至少两倍,所以时间复杂度为 \(O ( n + q \log w ) , w\)\(a _ { i }\) 的值域。

#include<bits/stdc++.h>
using namespace std;
mt19937 engine(chrono::steady_clock().now().time_since_epoch().count());
const int MAXN=3e5+5;
int n,q;
int nxt[MAXN];
long long a[MAXN],sum[MAXN],pre[MAXN],ppre[MAXN];
long long calc(int l,int r)
{
	if(l>r) return 0ll;
	return (ppre[r]-ppre[l-1])-pre[l-1]*(r-l+1);
}
void Init()
{
	stack<int>q;
	for(int i=2;i<=n;i++) {
		long long pA=a[i-1],pB=a[i];
		if(pA<=pB) {
			while(pA*2<=pB) pA*=2,sum[i]--;
		}
		else {
			while(pB<pA) pB*=2,sum[i]++;
		}
		pre[i]=pre[i-1]+sum[i];
		ppre[i]=ppre[i-1]+pre[i];
	}
	for(int i=n;i>=1;i--) {
		while(!q.empty()&&pre[q.top()]>=pre[i]) q.pop();
		nxt[i]=q.empty()?n+1:q.top();
		q.emplace(i);
	}
}
long long query(int l,int r)
{
	long long Ans=0;
	while(l<r) {
		int p=nxt[l];
		Ans+=calc(l+1,min(r,p-1));
		l=p;
	}
	return Ans;
}
int main()
{
	freopen("increasing.in","r",stdin);
	freopen("increasing.out","w",stdout);
	std::ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n>>q;
	for(int i=1;i<=n;i++) {
		cin>>a[i];
	}
	Init();
	while(q--) {
		int l,r;
		cin>>l>>r;
		cout<<query(l,r)<<"\n";
	}
	return 0;
}

当然也可以简单扫描线+线段树来做。

t2

这个题可做

AT_tenka1_2019_f Banned X - 洛谷

t3

这个题 hard

KOI 2021 Round 2] 美食推荐 - 洛谷

t4

似乎不可做

OOI 2025] Alice, Bob, and two arrays. - 洛谷

11.10 %你赛

t1

可做

NAPC-#1] Stage5 - Conveyors - 洛谷

t2

较困难

image

image

题解

image

t3

可做

CEOI 2015] 波将金的路径 (Day1) - 洛谷

t4

可以学习,不可做

JOI 2024 Final] 路网服务 2 / Road Service 2 - 洛谷

11.8 %你赛

t1

水水 trick

CCC 2018] 最大战略储备 - 洛谷

t2

简要题意:给定 \(n\) 条线段,第 \(i\) 条是 \([l_i, r_i]\),权值为 \(w_i\)。将他们染成红色或黑色,要求任意两条红色不相交且任意一条黑色至少和一条红色相交。请最小化红色线段的权值和,并输出这个权值和。两条线段 \([l_i, r_i], [l_j, r_j]\) 相交当且仅当存在 \(k \in [l_i, r_i]\)\(k \in [l_j, r_j]\)

对于所有测试数据,保证 \(1 \leq n \leq 5 \times 10^6\)\(0 \leq m < 2^{22}\)\(0 \leq l_i, r_i \leq m\)\(0 \leq w_i < 2^{32}\)

题解

原题:P8592 『JROI-8』颅脑损伤 2.0(加强版) - 洛谷(不完全一样)

[数据删除]说:"一场比赛不可能没有 dp!"

这道题 100% 是一道 dp!

基础思路

\(f_i\) 表示当第 \(i\) 条线段为红色时,前 \(i\) 根线段中红色线段的最小长度和。

得到状态转移方程:\(f_i = \min_j(f_i, f_j + w_i)\)

优化过程

\(O(n^3)\) 解法

在 dp 的 2 重循环基础上,再增加 1 重循环,用来枚举每根线段,以确定情况合不合法,如果合法就状态转移,不合法就继续。

\(O(n^2)\) 解法

预处理判断结果:设 \(g_{i,j}\) 表示当第 \(i\)\(j\) 根线段都为红色时,中间线段为黑色是否可行。用 \(O(n^2)\) 生成 \(g\) 数组,再和之前一样用 \(O(n^2)\) dp。

\(O(n \log n)\) 解法

观察到时间复杂度瓶颈在求 \(g\) 以及 \(f_i\) 上,考虑优化。

定义 \(pos_i = \max_{r_j < l_i}\{l_j\}\),即右端点严格小于 \(l_i\) 的线段中,左端点最大的线段的左端点的位置。

对于任意 \(j\),若 \(f_j\) 可以更新 \(f_i\),那么必须满足 \(r_j \geq pos_i\)\(r_j < l_i\)

所以状态转移方程变为:\(f_i = \min_{pos_i \leq r_j < l_i}\{f_j\}\)

用两次 RMQ 即可,一次求 \(pos\),一次求 \(f\)

\(O(n)\) 解法

对于 \(pos_i\),预先记录下每一个右端点对应的左端点,然后对于所有小于 \(l_i\) 的位置求其对应的左端点位置的最大值即为 \(pos_i\),计算前缀最大值即可。

对于 \(f\),由于 \(pos_i\)\(l_i\) 都单调递增,用单调队列代替线段树即可。

//Contestants only need to implement the solve() function, and any changes made to other parts will be at their own risk
#include<bits/stdc++.h> //Universal header file
using namespace std;
const int N=5e6+10; //n does not exceed 5e6
const int M=(1<<22)+10; //m does not exceed 4194303 
unsigned int n,m,l[N],r[N],w[N]; //Approximately 64MB of space has been occupied
long long ans; //Note that the answer may exceed the range of unsigned int
unsigned int seed; //Random seeds
unsigned int rnd(unsigned int &x); //Random number generator
inline void input(); //Input data
inline void output(); //Output the answer, note that the final answer needs to be stored in the variable ans
inline void solve(); //Calculate the answer
//Leave the part for contestants to define variables
unsigned int b[M];
long long f[M],q[N],ll,rr;
int tot,head[M],to[N],nxt[N];
int main(){
    freopen("rules.in","r",stdin); //This question requires opening a file for reading and writing
    freopen("rules.out","w",stdout);
    input();
    solve();
    output();
	return 0;
}
inline void add(int u, int v) {
	tot++;
	nxt[tot]=head[u];
	to[tot]=v;
	head[u]=tot;
	return;
}
inline void solve() { //Contestants only need to implement this function
	//Note that the following code can only pass test 1. Contestants pursuing higher scores need to delete the following three lines
	/*for(int i = 1; i <= n; i++) {
        ans += w[i];
    }*/
    m++;
    for(int i=1;i<=n;i++) {
    	l[i]++;
    	r[i]++;
	}
    n++;m++;
	l[n]=m;r[n]=m;w[n]=0;
	for(int i=1;i<=m;i++){
		b[i]=0;
		f[i]=0x3f3f3f3f3f3f3f3f;
	}
	for(int i=1;i<=n;i++){
		b[r[i]]=max(b[r[i]],l[i]);
		add(l[i],i);
	}
	ll=1;rr=1;q[1]=0;f[0]=0;
	for(int i=1;i<=m;i++){
		while(ll<=rr&&q[ll]<b[i-1])ll++;
		for(int k=head[i];k;k=nxt[k]){
			int j=to[k];
			f[r[j]]=min(f[r[j]],f[q[ll]]+w[j]);
		}
		while(ll<=rr&&f[q[rr]]>f[i])rr--;
		q[++rr]=i;
	}
	ans=f[m];
	return;
}
unsigned int rnd(unsigned int &x) { 
    x ^= (x << 13) & 0xFFFFFFF;
    x ^= (x >> 17);
    x ^= (x << 5) & 0xFFFFFFF;
    x += 100;
    return x;
}
inline void input() {
    scanf("%u%u%u", &n, &m, &seed);
    for (int i = 1; i <= n; i++) {
        l[i] = rnd(seed) & m;
        r[i] = rnd(seed) & m;
        w[i] = rnd(seed);
        if (l[i] > r[i]) {
            swap(l[i], r[i]);
        }
    }
    return;
}
inline void output() {
	printf("%lld\n", ans);
	return;
}

t3

难,可做

湖北省选模拟 2024] 沉玉谷 / jade - 洛谷

t4

难题

Занулити пiдвiдрiзок - Problem - QOJ.ac

A国正在进行改革,计划对 \(n\) 个沿海乡镇进行城市化改造,初始时 A国科技水平 \(x=0\),第 \(i\) 个乡镇的发展程度 \(b_i=0\),每年 A国可以进行 \(2\) 种改革举措中的一种:

  • 将科技水平增加 \(1\),即 \(x = x + 1\)
  • 选择一个 \(i\),使第 \(i\) 个乡镇的发展程度异或上科技水平,即 \(b_i = b_i \oplus x\)

当第 \(i\) 个乡镇的发展程度恰好为 \(a_i\) 时,这个乡镇就完成了城市化改造,可以修建港口。

但 A国经费有限,不能在所有乡镇都修建港口,因此 A国的总设计师决定让一批乡镇先完成通商。具体的,他给了你 \(q\) 次询问,每次询问给定一个区间 \([l, r]\),你需要回答至少需要多少年才能在 \([l, r]\) 区间中的每个乡镇都建立港口。

部分子任务强制在线

题解

由于 \(x\) 必须达到区间中的数的最高位,可以发现每个数最多只需要操作两次。只考虑区间内含有最高位 \(k\) 的数并统一减去 \(2^k\),问题转化为求解 \(\min(x + \sum[a_i > x])\)

将上式写作 \(\max(\sum[a_i \leq x] - x)\),逆用 Hall 定理,将问题转化为二分图匹配问题,左部点 \(i\) 向右部点 \(j \leq a_i\) 连边,上式即为左部点失配点数。

由于所有左部点均向右部一个前缀连边,故可以以任意顺序贪心匹配,不妨按照 \(r, r-1, \ldots, l\) 的顺序进行匹配。

按右端点进行扫描线,扫到 \(r\) 时,由于我们从右至左贪心匹配,故一定匹配 \(r\)。使用 Hall 定理维护并判断是否可成功加入 \(r\),只需要判断是否满足 \(\min(x - \sum[a_i \leq x]) \geq 0\) 即可。

若不能直接匹配,则需要找到最左侧的匹配点 \(l\) 使得删去 \(l\) 后存在完美匹配。这只需要找到最小的 \(x\) 使得 \((x - \sum[a_i \leq x]) < 0\),那么 \(l\) 即为最小的满足 \(a_l \leq x\) 的位置。

上述操作均可使用线段树简单维护,而询问只需使用主席树维护矩形加单点求值。

精细实现可做到时间复杂度 \(O((n+q) \log n)\)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+10,K=60+2,INF=1e9,mod=19491001;
int n,k,q,t,lg[N],pw[65],rx[N],sp[N][K],cp[K],ct[K],pol[N],nx[N],rt[N][K];
ll a[N],b[N],las,st[19][N];
inline ll querymx(int l,int r){
    int t=__lg(r-l+1);
    return max(st[t][l],st[t][r-(1<<t)+1]);
}
struct Segment_Trees{
    #define ls (a[rt].lson)
    #define rs (a[rt].rson)
    struct node{
        int lson,rson,cnt;
    }a[N*20];
    int cnt;
    inline int copy(int x){
        int rt=++cnt;
        a[rt]=a[x];
        return rt;
    }
    inline void insert(int &rt,int l,int r,int p){
        rt=copy(rt),a[rt].cnt++;
        if(l==r) return ;
        int mid=l+r>>1;
        if(p<=mid) insert(ls,l,mid,p);
        else insert(rs,mid+1,r,p);
    }
    inline int query(int rt,int l,int r,int L,int R){
        if(!rt) return 0;
        if(L<=l&&r<=R) return a[rt].cnt;
        int mid=l+r>>1,ans=0;
        if(L<=mid) ans+=query(ls,l,mid,L,R);
        if(R>mid) ans+=query(rs,mid+1,r,L,R);
        return ans;
    }
}Ts;
struct Segment_Tree{
    #define ls (rt<<1)
    #define rs (rt<<1|1)
    int mn[N<<2],mnp[N<<2],tag[N<<2];
    inline void pushup(int rt){
        mn[rt]=min(mn[ls],mn[rs])+tag[rt];
        mnp[rt]=min(mnp[ls],mnp[rs]);
    }
    inline void pushtag(int rt,int v){
        tag[rt]+=v,mn[rt]+=v;
    }
    inline void build(int rt,int l,int r){
        if(l==r){ mn[rt]=rx[l],mnp[rt]=INF;return ; }
        int mid=l+r>>1;
        build(ls,l,mid),build(rs,mid+1,r);
        pushup(rt);
    }
    inline void modify(int rt,int l,int r,int p,int v){
        if(l==r){ mnp[rt]=v;return ; }
        int mid=l+r>>1;
        if(p<=mid) modify(ls,l,mid,p,v);
        else modify(rs,mid+1,r,p,v);
        pushup(rt);
    }
    inline void add(int rt,int l,int r,int L,int R,int v){
        if(L<=l&&r<=R) return pushtag(rt,v);
        int mid=l+r>>1;
        if(L<=mid) add(ls,l,mid,L,R,v);
        if(R>mid) add(rs,mid+1,r,L,R,v);
        pushup(rt);
    }
    inline int query1(int rt,int l,int r,int L,int R){
        if(L<=l&&r<=R) return mn[rt];
        int mid=l+r>>1,ans=INF;
        if(L<=mid) ans=min(ans,query1(ls,l,mid,L,R));
        if(R>mid) ans=min(ans,query1(rs,mid+1,r,L,R));
        return ans+tag[rt];
    }
    inline int query2(int rt,int l,int r,int L,int R){
        if(L<=l&&r<=R) return mnp[rt];
        int mid=l+r>>1,ans=INF;
        if(L<=mid) ans=min(ans,query2(ls,l,mid,L,R));
        if(R>mid) ans=min(ans,query2(rs,mid+1,r,L,R));
        return ans;
    }
    inline int find(int rt,int l,int r,int L,int R,int tg=0){
        if(l>R||r<L||mn[rt]+tg>=0) return -1;
        if(l==r) return l;
        tg+=tag[rt];
        int mid=l+r>>1,tp=find(ls,l,mid,L,R,tg);
        if(tp==-1) tp=find(rs,mid+1,r,L,R,tg);
        return tp;
    }
 }T;
vector<ll>vec;
ll ask(int l,int r){
    int t=__lg(querymx(l,r));
    return (1ll<<t)+(r-l+1)+(sp[r][t]-sp[l-1][t])-Ts.query(rt[r][t],1,n,l,r);
}
int main()
{
    freopen("city.in","r",stdin);
    freopen("city.out","w",stdout);
    cin.tie(0)->sync_with_stdio(0);
    cin>>n>>n>>q>>t;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        vec.push_back(a[i]);
        lg[i]=__lg(a[i]),k=max(k,lg[i]);
        b[i]=a[i]-(1ll<<lg[i]);
    }
    for(int i=1;i<=n;i++)
        st[0][i]=a[i];
    for(int i=1;i<=18;i++)
        for(int j=1;j+(1<<i)-1<=n;j++)
            st[i][j]=max(st[i-1][j],st[i-1][j+(1<<i-1)]);
    for(int i=1;i<=n;i++){
        for(int j=0;j<=k;j++)
            sp[i][j]=sp[i-1][j];
        sp[i][lg[i]]++;
    }
    for(int j=0;j<=k;j++)
        ct[j]=sp[n][j],cp[j]+=ct[j],cp[j+1]+=cp[j];
    for(int i=1;i<=n;i++)
        if(b[i]>=ct[lg[i]]) b[i]=-1;
        else b[i]=(lg[i]?cp[lg[i]-1]+1:1)+b[i];
    for(int i=0,p=0;i<=k;i++)
        for(int j=0;j<ct[i];j++)
            rx[++p]=j;
    T.build(1,1,n);
    for(int i=1;i<=n;i++)
        pol[i]=n+1;
    for(int i=n;i;i--)
        if(b[i]!=-1)
            nx[i]=pol[b[i]],pol[b[i]]=i;
    for(int i=1;i<=n;i++)
        pol[i]=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=k;j++)
            rt[i][j]=rt[i-1][j];
        if(b[i]==-1) continue;
        int L=lg[i]?cp[lg[i]-1]+1:1,R=cp[lg[i]];
        T.add(1,1,n,b[i],R,-1);
        if(!pol[b[i]]) T.modify(1,1,n,b[i],i),pol[b[i]]=i;
        int mnp=T.find(1,1,n,L,R);
        if(mnp==-1) continue;
        int lp=T.query2(1,1,n,L,mnp);
        T.add(1,1,n,b[lp],R,1);
        int nxp=nx[pol[b[lp]]];
        if(nxp>i) pol[b[lp]]=0,T.modify(1,1,n,b[lp],INF);
        else pol[b[lp]]=nxp,T.modify(1,1,n,b[lp],nxp);
        Ts.insert(rt[i][lg[i]],1,n,lp);
    }
    ll ans=0,x=0;
    for(int i=1;i<=q;i++)
    {
        int l,r;
        cin>>l>>r;
        if(t==2)l^=ans,r^=ans;
        ans=ask(l,r)%mod;
        x^=ans;
    }
    printf("%lld\n",x);
}
posted @ 2025-11-11 15:19  NeeDna  阅读(4)  评论(0)    收藏  举报