[UOJ46][清华集训2014]玄学

uoj

description

给出\(n\)个变换,第\(i\)个变换是将区间中\(l_i,r_i\)的数\(x\)变成\((a_ix+b_i)\mod m\)。
每次会新增一个变换,或者查询询问如果进行编号\([s,t]\)的操作,第\(k\)个数会变成多少。
\(n\le10^5,q\le6\times10^5\)

sol

二进制分组。
按顺序把变化插入线段树,如果线段树的某个满了就向上归并两个儿子的变换。
\(a_j(a_ix+b_i)+b_j=a_ia_jx+a_jb_i+b_j\)就是两个变换的合并。
查询的时候找到线段树的对应节点(这些节点一定都是合并好了的),然后在维护出的变化序列上二分找到第\(k\)个位置,直接算即可。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 1e5+5;
struct node{int p,a,b;}t[N*30];
int Type,n,m,q,mod,val[N],L[N<<2],R[N<<2],cnt,ans;
void modify(int x,int l,int r,int ql,int qr,int a,int b){
    if (l==r){
        L[x]=cnt+1;
        if (ql>1) t[++cnt]=(node){ql-1,1,0};
        t[++cnt]=(node){qr,a,b};
        if (qr<n) t[++cnt]=(node){n,1,0};
        R[x]=cnt;return;
    }
    int mid=l+r>>1;
    if (m<=mid) modify(x<<1,l,mid,ql,qr,a,b);
    else modify(x<<1|1,mid+1,r,ql,qr,a,b);
    if (m<r) return;
    int i=L[x<<1],j=R[x<<1],u=L[x<<1|1],v=R[x<<1|1];
    L[x]=cnt+1;
    while (i<=j&&u<=v){
        t[++cnt]=(node){min(t[i].p,t[u].p),1ll*t[i].a*t[u].a%mod,(1ll*t[i].b*t[u].a+t[u].b)%mod};
        if (t[i].p==t[u].p) ++i,++u;else if (t[i].p<t[u].p) ++i;else ++u;
    }
    R[x]=cnt;
}
void calc(int x,int k){
    int l=L[x],r=R[x],res=0;
    while (l<=r){
        int mid=l+r>>1;
        if (t[mid].p>=k) res=mid,r=mid-1;
        else l=mid+1;
    }
    ans=(1ll*ans*t[res].a+t[res].b)%mod;
}
void query(int x,int l,int r,int ql,int qr,int k){
    if (l>=ql&&r<=qr) {calc(x,k);return;}
    int mid=l+r>>1;
    if (ql<=mid) query(x<<1,l,mid,ql,qr,k);
    if (qr>mid) query(x<<1|1,mid+1,r,ql,qr,k);
}
int main(){
    Type=gi()&1;n=gi();mod=gi();
    for (int i=1;i<=n;++i) val[i]=gi();
    q=gi();while (q--){
        int op=gi(),l=gi(),r=gi();
        if (Type) l^=ans,r^=ans;
        if (op==1){
            int a=gi(),b=gi();++m;
            modify(1,1,100000,l,r,a,b);
        }else{
            int k=gi();if (Type) k^=ans;
            ans=val[k];
            query(1,1,100000,l,r,k);
            printf("%d\n",ans);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/zhoushuyu/p/9452267.html

时间: 2024-08-30 12:46:19

[UOJ46][清华集训2014]玄学的相关文章

uoj #46[清华集训2014]玄学

uoj 因为询问是关于一段连续区间内的操作的,所以对操作构建线段树,这里每个点维护若干个不交的区间,每个区间\((l,r,a,b)\)表示区间\([l,r]\)内的数要变成\(ax+b\) 每次把新操作加入线段树中下一个叶子,然后如果某个节点里所有操作都加进去了,就条到父亲,把两个儿子的信息合并到父亲上.这里合并就是把两个区间集合合并成一个,例如两个区间\([a,c]\)和\([b,d](a\le b\le c\le d)\)会合并成\([a,b),[b,c),[c,d]\).合并出来的区间如果

清华集训2014 做题记录

清华集训2014做题记录 已完成 [清华集训2014]玛里苟斯 [清华集训2014]主旋律 [清华集训2014]奇数国 [清华集训2014]矩阵变换 [清华集训2014]sum [清华集训2014]虫逢 [清华集训2014]玄学 [清华集训2014]文学 未完成 [清华集训2014]卡常数 [清华集训2014]简单回路 [清华集训2014]Router [清华集训2014] Breaking Bomber 写一题要膜一题题解,膜完题解膜代码,膜完代码膜指导,膜了好几天了还有四个题没做. [清华集

uoj 41 【清华集训2014】矩阵变换 婚姻稳定问题

[清华集训2014]矩阵变换 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://uoj.ac/problem/41 Description 给出一个 N 行 M 列的矩阵A, 保证满足以下性质: M>N.    矩阵中每个数都是 [0,N] 中的自然数.    每行中, [1,N] 中每个自然数都恰好出现一次.这意味着每行中 0 恰好出现 M−N 次.    每列中,[1,N] 中每个自然数至多出现一次. 现在我们要在每行中选取一个非零数,

AC日记——【清华集训2014】奇数国 uoj 38

#38. [清华集训2014]奇数国 思路: 题目中的number与product不想冲: 即为number与product互素: 所以,求phi(product)即可: 除一个数等同于在模的意义下乘以一个数的逆元: 代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define maxn 100005

「清华集训2014」主旋律

「清华集训2014」主旋律 这个题好难难啊,我想了一个小时连50分都不会,只能去摸周指导了. 我们试图直接爆算集合 \(S\) 的非强连通导出子图数量,考虑将这个导出子图的所有强连通分量缩点后,一定是一个点数 \(\geq 2\) 的 \(\text{DAG}\) .即缩完以后至少要有一个入度为 \(0\) 的点,这个条件充分性显然,必要性考虑如果不存在入度为 \(0\) 的点,那么一定存在一个环,说明并没有将所有强连通分量缩掉.然后我们枚举入度为 \(0\) 的点集 \(T\) ,记 \(F(

「清华集训2014」矩阵变换

「清华集训2014」矩阵变换 解题思路 问题转化为找一个行与选的数字的完美匹配,记 \(pos[i][j]\) 为数字 \(i\) 在第 \(j\) 行的出现位置,要求不存在匹配边 \((a,b),(c,d)\) 使得 \(pos[b][a] < pos[b][c]\ \& \ pos[d][c] > pos[b][c]\) . 观察发现,这个式子相当于让一条边的权值看做 \(pos[x][y]\) ,当存在一个行与一个数没有匹配但是强行让它们匹配后数的权值会变大,行的权值会变小,这个

UOJ#46. 【清华集训2014】玄学

传送门 分析 清华集训真的不是人做的啊嘤嘤嘤 我们可以考虑按操作时间把每个操作存进线段树里 如果现在点x正好使一个整块区间的右端点则更新代表这个区间的点 我们不难发现一个区间会因为不同的操作被分成若干块,每块对应序列上不同的区间 于是查询时对于每个线段树上区间查询时二分查找当前点在哪一块中即可 代码 #include<iostream> #include<cstdio> #include<cstring> #include<string> #include&

UOJ46. 【清华集训2014】玄学

传送门 Sol 考虑对于操作时间建立线段树,二进制分组 那么现在主要的问题就是怎么合并信息 你发现一个性质,就是每个修改只会在整个区间内增加两个端点 那么我们二进制分组可以得到每个区间内最多只有区间长度级别段,每一段的修改都是一样的 那么可以直接一层层归并上来 最后询问就是二分每一个线段树的节点的询问段即可 修改复杂度 \(\Theta(n log n)\) 询问复杂度 \(\Theta(n log^2 n)\) # include <bits/stdc++.h> using namespac

【UOJ #46】 【清华集训2014】玄学

题目描述 巨酱有 n 副耳机,他把它们摆成了一列,并且由 1 到n依次编号.每个耳机有一个玄学值,反映了各自的一些不可名状的独特性能.玄学值都是 0 到 m-1 间的整数.在外界的作用下(包括但不限于换线.上放.更换电源为核电.让kAc叔叔给它们讲故事),这些耳机的玄学值会发生改变.特别地,巨酱观察发现,每种作用 o 对应了两个整数 ao与 bo,在这种作用之后,玄学值原本为 x 的耳机,其玄学值恰会变成 (aox+bo)modm. 巨酱对他手头耳机的表现并不满意,遗憾的是,最近他并不有钱,无法