2017: #6282. 数列分块入门 6
题目描述
给出一个长为 nnn 的数列,以及 nnn 个操作,操作涉及单点插入,单点询问,数据随机生成。
输入
第一行输入一个数字 nnn。
第二行输入 nnn 个数字,第 i 个数字为 aia_iai,以空格隔开。
接下来输入 nnn 行询问,每行输入四个数字 opt\mathrm{opt}opt、lll、rrr、ccc,以空格隔开。
若 opt=0\mathrm{opt} = 0opt=0,表示在第 lll 个数字前插入数字 rrr (ccc 忽略)。
若 opt=1\mathrm{opt} = 1opt=1,表示询问 ara_rar 的值(lll 和 ccc 忽略)。
输出
对于每次询问,输出一行一个数字表示答案。
样例输入
1 2 2 3
0 1 3 1
1 1 4 4
0 1 2 2
1 1 2 4
样例输出
2
3
提示
对于 100% 100\%100% 的数据,1≤n≤100000,−231≤others 1 \leq n \leq 100000, -2^{31} \leq \mathrm{others}1≤n≤100000,−231≤others、ans≤231−1 \mathrm{ans} \leq 2^{31}-1ans≤231−1。
#include<cstdio>
#include<queue>
#include<vector>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<cmath>
#define _ 0
#define LL long long
#define Space putchar(' ')
#define Enter putchar('\n')
#define fuu(x,y,z) for(int x=(y),x##end=z;x<=x##end;x++)
#define fu(x,y,z) for(int x=(y),x##end=z;x<x##end;x++)
#define fdd(x,y,z) for(int x=(y),x##end=z;x>=x##end;x--)
#define fd(x,y,z) for(int x=(y),x##end=z;x>x##end;x--)
#define mem(x,y) memset(x,y,sizeof(x))
#ifndef olinr
inline char getc()
{
static char buf[100001],*p1=buf,*p2=buf;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100001,stdin),p1==p2)? EOF:*p1++;
}
#else
#define getc() getchar()
#endif
template<typename T>inline void in(T &x)
{
int f=1; char ch; x=0;
while(!isdigit(ch=getc()))(ch=='-')&&(f=-f);
while(isdigit(ch)) x=x*10+(ch^48),ch=getc();
x*=f;
}
const int inf=0x7fffffff;
int n,num;
struct K
{
int a[2100],siz;
K *nxt;
K() {mem(a,0),siz=0,nxt=NULL;}
void *operator new (size_t)
{
static K *S=NULL,*T=NULL;
return (S==T&&(T=(S=new K[1024])+1024),S++);
}
};
K *beg=new K();
inline void init()
{
in(n),num=std::sqrt(n);
K *o=beg,*t;
fuu(i,1,n)
{
if(o->siz==num) t=new K(),o->nxt=t,o=t;
in(o->a[++o->siz]);
}
}
inline void split(K *o)
{
K *t=new K();
int mid=(1+o->siz)>>1;
fuu(i,mid+1,o->siz) t->a[++t->siz]=o->a[i];
o->siz=mid;
t->nxt=o->nxt,o->nxt=t;
}
inline void add(int pos,int key)
{
K *o=beg;
int now=0;
while(now+o->siz<pos) now+=o->siz,o=o->nxt;
int p=pos-now;
fdd(i,o->siz,p+1) o->a[i+1]=o->a[i];
o->a[p+1]=key;
o->siz++;
if(o->siz>=(num<<1)) split(o);
}
inline int query(int pos)
{
K *o=beg;
int now=0;
while(now+o->siz<pos) now+=o->siz,o=o->nxt;
return o->a[pos-now];
}
int main()
{
init();
int p,l,r,c;
while(n--)
{
in(p),in(l),in(r),in(c);
if(p==0) add(l-1,r);
else printf("%d\n",query(r));
}
return ~~(0^_^0);
}
2018: #6283. 数列分块入门 7
题目描述
给出一个长为 nnn 的数列,以及 nnn 个操作,操作涉及区间乘法,区间加法,单点询问。
输入
第一行输入一个数字 nnn。
第二行输入 nnn 个数字,第 i 个数字为 aia_iai,以空格隔开。
接下来输入 nnn 行询问,每行输入四个数字 opt\mathrm{opt}opt、lll、rrr、ccc,以空格隔开。
若 opt=0\mathrm{opt} = 0opt=0,表示将位于 [l,r][l, r][l,r] 的之间的数字都加 ccc。
若 opt=1\mathrm{opt} = 1opt=1,表示将位于 [l,r][l, r][l,r] 的之间的数字都乘 ccc。
若 opt=2\mathrm{opt} = 2opt=2,表示询问 ara_rar 的值 mod 10007mod \ 10007mod10007(lll 和 ccc 忽略)。
输出
对于每次询问,输出一行一个数字表示答案。
样例输入
7
1 2 2 3 9 3 2
0 1 3 1
2 1 3 1
1 1 4 4
0 1 7 2
1 2 6 4
1 1 6 5
2 2 6 4
样例输出
3
100
提示
对于 100% 100\%100% 的数据,1≤n≤100000,−231≤others 1 \leq n \leq 100000, -2^{31} \leq \mathrm{others}1≤n≤100000,−231≤others、ans≤231−1 \mathrm{ans} \leq 2^{31}-1ans≤231−1。
#include<bits/stdc++.h>
using namespace std;
# define ll long long
const int maxn = 2e6+100;
const int inf = 0x3f3f3f3f;
const int mod = 1e4+7;
ll l[maxn],r[maxn],belong[maxn];
ll add[maxn],a[maxn],sum[maxn],mul[maxn];
int n;
void buildblock()
{
ll tmp=(ll)sqrt(n);
ll tot=n/tmp;
if(n%tmp)
tot++;
for(ll i=1; i<=n; i++)
{
belong[i]=(i-1)/tmp+1ll;
}
for(ll i=1; i<=tot; i++)
{
l[i]=(i-1)*tmp+1ll;
r[i]=i*tmp;
}
r[tot]=n;
}
void down(int pos)
{
for(int i=l[pos]; i<=r[pos]; i++)
{
a[i]=(a[i]*mul[pos]+add[pos]+mod)%mod;
}
mul[pos]=1;
add[pos]=0;
}
void update1(ll st,ll ed,ll val)
{
if(belong[st]==belong[ed])
{
down(belong[st]);
for(ll i=st; i<=ed; i++)
a[i]+=val,a[i]%=mod;
return ;
}
down(belong[st]);
for(ll i=st; i<=r[belong[st]]; i++)
a[i]+=val,a[i]%=mod;
down(belong[ed]);
for(ll i=l[belong[ed]]; i<=ed; i++)
a[i]+=val,a[i]%=mod;
for(ll i=belong[st]+1; i<belong[ed]; i++)
add[i]+=val,add[i]%=mod;
}
void update2(ll st,ll ed,ll val)
{
if(belong[st]==belong[ed])
{
down(belong[st]);
for(ll i=st; i<=ed; i++)
a[i]*=val,a[i]=a[i]%mod;
return ;
}
down(belong[st]);
for(ll i=st; i<=r[belong[st]]; i++)
a[i]*=val,a[i]%=mod;
down(belong[ed]);
for(ll i=l[belong[ed]]; i<=ed; i++)
a[i]*=val,a[i]%=mod;
for(ll i=belong[st]+1; i<belong[ed]; i++)
mul[i]*=val,add[i]*=val,mul[i]%=mod,add[i]%=mod;
}
ll ask(ll pos)
{
return (a[pos]*mul[belong[pos]]%mod+add[belong[pos]])%mod;
}
int main()
{
memset(add,0,sizeof(add));
for(int i=0; i<maxn; i++)
mul[i]=1;
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%lld",&a[i]);
}
buildblock();
ll op,st,ed;
ll val;
while(n--)
{
scanf("%lld %lld %lld %lld",&op,&st,&ed,&val);
if(op==0)
{
update1(st,ed,val);
}
else if(op==1)
{
update2(st,ed,val);
}
else if(op==2)
{
printf("%lld\n",ask(ed)%mod);
}
}
return 0;
}
2019: #6284. 数列分块入门 8
题目描述
给出一个长为 nnn 的数列,以及 nnn 个操作,操作涉及区间询问等于一个数 ccc 的元素,并将这个区间的所有元素改为 ccc。
输入
第一行输入一个数字 nnn。
第二行输入 nnn 个数字,第 i 个数字为 aia_iai,以空格隔开。
接下来输入 nnn 行询问,每行输入三个数字 lll、rrr、ccc,以空格隔开。
表示先查询位于 [l,r][l,r][l,r] 的数字有多少个是 ccc,再把位于 [l,r][l,r][l,r] 的数字都改为 ccc。
输出
对于每次询问,输出一行一个数字表示答案。
样例输入
4
1 2 2 4
1 3 1
1 4 4
1 2 2
1 4 2
样例输出
1
1
0
2
提示
对于 100% 100\%100% 的数据,1≤n≤100000,−231≤others 1 \leq n \leq 100000, -2^{31} \leq \mathrm{others}1≤n≤100000,−231≤others、ans≤231−1 \mathrm{ans} \leq 2^{31}-1ans≤231−1。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <set>
#include <map>
using namespace std;
#define inf 0x7f7f7f7f
#define maxn 100050
#define N 100100
#define P 2
typedef long long ll;
typedef struct {
int u, v, next, w;
} Edge;
Edge e[2];
int cnt, head[1];
inline void add1(int u, int v, int w) {
e[cnt].u = u;
e[cnt].v = v;
e[cnt].w = w;
// e[cnt].f=f;
e[cnt].next = head[u];
head[u] = cnt++;
e[cnt].u = v;
e[cnt].v = u;
e[cnt].w = w;
// e[cnt].f=-f;
e[cnt].next = head[v];
head[v] = cnt++;
}
inline void write(int x) {
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
inline int read() {
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
int opt, l, r, c, sz, n, pos[maxn];
int a[maxn], mark[maxn];
// vector<ll>vec[505];
inline void judge(int t) {
mark[t] = 0;
for (int i = t * sz + 1; t <= min(n, (t + 1) * sz) - 1; i++)
if (a[i] != a[i + 1]) {
mark[t] = 1;
break;
}
}
int query(int l, int r, int val) {
int res = 0, pl = pos[l], pr = pos[r];
if (mark[pl]) {
for (int i = pl * sz + 1; i <= min(n, (pl + 1) * sz); i++) a[i] = mark[pl];
if (val != mark[pl])
mark[pl] = 0;
}
for (int i = l; i <= min(r, (pl + 1) * sz); i++)
if (a[i] == val)
res++;
else
a[i] = val;
if (pl != pr) {
if (mark[pr]) {
for (int i = pr * sz + 1; i <= min(n, (pr + 1) * sz); i++) a[i] = mark[pr];
if (val != mark[pr])
mark[pr] = 0;
}
for (int i = pr * sz + 1; i <= r; i++) {
if (a[i] == val)
res++;
else
a[i] = val;
}
}
for (int i = pl + 1; i < pr; i++) {
if (mark[i]) {
if (mark[i] == val)
res += sz;
else
mark[i] = val;
} else {
for (int j = i * sz + 1; j <= (i + 1) * sz; j++) {
if (a[j] == val)
res++;
else
a[j] = val;
}
mark[i] = val;
}
}
return res;
}
int main() {
cin >> n;
sz = sqrt(n);
for (int i = 1; i <= n; i++) {
a[i] = read();
pos[i] = (i - 1) / sz;
}
for (int i = 1; i <= n; i++) {
l = read(), r = read(), c = read();
printf("%d\n", query(l, r, c));
}
return 0;
}
2020: #6285. 数列分块入门 9
题目描述
给出一个长为 nnn 的数列,以及 nnn 个操作,操作涉及询问区间的最小众数。
输入
第一行输入一个数字 nnn。
第二行输入 nnn 个数字,第 i 个数字为 aia_iai,以空格隔开。
接下来输入 nnn 行询问,每行输入两个数字 lll、rrr,以空格隔开。
表示查询位于 [l,r][l,r][l,r] 的数字的众数。
输出
对于每次询问,输出一行一个数字表示答案。
样例输入
4
1 2 2 4
1 2
1 4
2 4
3 4
样例输出
1
2
2
2
提示
对于 100% 100\%100% 的数据,1≤n≤100000,−231≤others 1 \leq n \leq 100000, -2^{31} \leq \mathrm{others}1≤n≤100000,−231≤others、ans≤231−1 \mathrm{ans} \leq 2^{31}-1ans≤231−1。
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<cstdio>
#include<cstring>
#include<cmath>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int a[maxn],MAX[2020][2020],id,n,block;
int pos[maxn],val[maxn],num[maxn];//pos记录在哪个块,val[i]记录编号为i的原值,num用来求众数
map<int,int >mp;
vector<int>v[maxn];//储存编号相同,即数相同时的原来的第几个值。用来二分求众数用
bool visit[maxn];//标记
void init(int x)//预处理每块之间的众数
{
int count=0,max1=0;
memset(num,0,sizeof(num));
for(int i=(x-1)*block+1;i<=n;i++)
{
num[a[i]]++;
if(num[a[i]]>count||num[a[i]]==count&&val[a[i]]<val[max1])//找最小众数
{
count=num[a[i]];
max1=a[i];
}
MAX[x][pos[i]]=max1;
}
}
int er(int x,int l,int r)//二分查找块内,编号为x的数量
{
return upper_bound(v[x].begin(),v[x].end(),r)-lower_bound(v[x].begin(),v[x].end(),l);
}
int solve(int l,int r)
{
int ans=MAX[pos[l]+1][pos[r]-1],count=0;//中间一整块的众数
memset(visit,0,sizeof(visit));
count=er(ans,l,r);
visit[ans]=1;
for(int i=l;i<=min(pos[l]*block,r);i++)//暴力左边不完整块中每个数是否可能为众数
{
if(visit[a[i]]==0)
{
visit[a[i]]=1;
int count1=er(a[i],l,r);
if(count1>count||count1==count&&val[a[i]]<val[ans])
{
count=count1;
ans=a[i];
}
}
}
if(pos[l]!=pos[r])//暴力右边不完整块中每个数是否可能为众数
{
for(int i=(pos[r]-1)*block+1;i<=r;i++)
{
if(visit[a[i]]==0)
{
visit[a[i]]=1;
int count1=er(a[i],l,r);
if(count1>count||count1==count&&val[a[i]]<val[ans])
{
count=count1;
ans=a[i];
}
}
}
}
return val[ans];
}
int main()
{
scanf("%d",&n);
block=80;
id=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pos[i]=(i-1)/block+1;
if(mp[a[i]]==0)
{
mp[a[i]]=++id;//id第几个出现的数
val[id]=a[i];//保存原始值
}
a[i]=mp[a[i]];//保存编号(第几个出现的数)
v[a[i]].push_back(i);
}
for(int i=1;i<=pos[n];i++)
init(i);
int l,r;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&l,&r);
printf("%d\n",solve(l,r));
}
return 0;
}