Ynoi2019模拟赛
前言
太毒瘤了!!!
感觉都是经典的问题,但以前从没想过有什么更优的办法。。
果然是我太菜了吗。。。
出题人lxl的题解
lxl的题解已经很详细了,我就直接贴代码吧。。
Yuno loves sqrt technology I
链接
P5046 Yuno loves sqrt technology I
吐槽
第一遍写完之后果不其然被卡成20分。
看了下洛谷的题解感觉有些常数应该比我做法大的方法都过了,感觉不可思议,贴了一个题解的代码发现也被卡成20.。
怀疑洛谷的测评姬变慢了。。好多题解的代码都过不了。。
然后只能自己卡常,交了5页测评。。。还好最后还是过了。。
(Code)
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e5+10;
inline int Read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9'){ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x;
}
inline LL read(){
LL x=0;char ch=getchar();
while(ch<'0'||ch>'9'){ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x;
}
inline void print(LL x){
if(x>9) print(x/10);
putchar(x%10+'0');
}
const int B=520;
const int T=193;
int n,m;
int a[N],bl[N],pos[N],t[N];
int pre[T+5][B+5],suf[T+5][B+5],c[T+5][B+5],sz[T+5];
int s[T+5][N];
inline void add(int x,int w){while(x<=n){t[x]+=w;x+=x&-x;} }
inline int get(int x){int re=0;while(x){re+=t[x];x-=x&-x;} return re;}
LL F[T+5][T+5];
int main(){
n=Read();m=Read();
for(register int i=1;i<=n;++i) {
a[i]=Read();
bl[i]=(i+B-1)/B;
c[bl[i]][sz[bl[i]]++]=a[i];
pos[a[i]]=i;
}
LL l,r;
int x,y,z;
for(register int i=1;i<=bl[n];++i){
sort(c[i],c[i]+sz[i]);
l=(i-1)*B+1;
r=min(i*B,n);
for(register int j=l,k=1;j<=r;++j,++k){
add(a[j],1);
pre[i][k]=k-get(a[j])+pre[i][k-1];
}
for(register int j=l,k=1;j<=r;++j,++k) add(a[j],-1);
for(register int j=r,k=1;j>=l;--j,++k){
suf[i][k]=get(a[j])+suf[i][k-1];
add(a[j],1);
}
for(register int j=r,k=1;j>=l;--j,++k) add(a[j],-1);
}
for(register int i=1;i<bl[n];++i){
int k=0;
for(register int j=1;j<=n;++j){
while(c[i][k]<j&&k<sz[i]) ++k;
if(bl[pos[j]]<i)s[i][pos[j]]=k;
else if(bl[pos[j]]>i) s[i][pos[j]]=B-k;
}
for(register int j=1;j<=n;j+=4) {
s[i][j]+=s[i][j-1];
s[i][j+1]+=s[i][j];
s[i][j+2]+=s[i][j+1];
s[i][j+3]+=s[i][j+2];
}
}
for(register int i=1;i<bl[n];++i){
for(register int j=i+1;j<bl[n];++j){
F[i][j]=s[j][j*B]-s[j][(i-1)*B]+F[i][j-1];
}
}
LL ans=0;int itl,itr;
for(register int tt=1;tt<=m;++tt){
l=read();r=read();
l^=ans;r^=ans;
if(bl[l]==bl[r]){
x=bl[l];y=0;
ans=pre[x][r-(x-1)*B]-pre[x][l-1-(x-1)*B];
for(register int i=0;i<sz[x];++i){
if(pos[c[x][i]]<l) ans-=y;
else if(pos[c[x][i]]<=r) ++y;
}
}
else{
y=0;x=bl[l];z=bl[r];
ans=suf[x][x*B-l+1]+pre[z][r-(z-1)*B]+F[x+1][z-1];
itl=x*B;itr=(z-1)*B;
for(register int i=x+1;i<z;++i)
ans+=pre[i][B]+s[i][itl]-s[i][l-1]+s[i][r]-s[i][itr];
for(itl=0,itr=0;itl<sz[x];++itl){
for(;itr<sz[z]&&c[x][itl]>c[z][itr];++itr) if(pos[c[z][itr]]<=r) ++y;
if(pos[c[x][itl]]>=l) ans+=y;
}
}
print(ans);puts("");
}
return 0;
}
Yuno loves sqrt technology II
链接
P5047 Yuno loves sqrt technology II
吐槽
做法类似第十四分块,二次离线时要用根号平衡。。
for比while更快,然而写莫队不用while就非常蛋疼。。。
卡常过程比第一题更熟练了,只交了两页测评。。
(Code)
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e5+10;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void print(LL x){
if(x>9) print(x/10);
putchar(x%10+'0');
}
int B;
int n,m,cnt;
int a[N],b[N],g[N],h[N];
int find(int x){
int l=1,r=cnt,mid;
while(1){
mid=l+r>>1;
if(b[mid]==x) return mid;
else if(b[mid]<x) l=mid+1;
else r=mid-1;
}
}
struct query{
int l,r,id;
}q[N];
struct node{
int l,r,f,id,op;
};
vector<node> p[N];
bool cmpq(query x,query y){
return g[x.l]!=g[y.l]?g[x.l]<g[y.l]:x.r<y.r;
}
int xiao[N],da[N],t[N];
inline void add(int x){
for(;x<=n;x+=x&-x)++t[x];
}
inline int get(int x){
int re=0;
for(;x;x-=x&-x) re+=t[x];
return re;
}
LL ans[N];
int main(){
n=read();m=read();B=sqrt(n);
for(register int i=1;i<=n;++i){
a[i]=read();
b[i]=a[i];
g[i]=(i+B-1)/B;
}
sort(b+1,b+1+n);
cnt=1;
for(register int i=2;i<=n;++i)
if(b[i]!=b[i-1]) b[++cnt]=b[i];
for(register int i=1;i<=n;++i) a[i]=find(a[i]);
for(register int i=1;i<=n;++i){
add(a[i]);
xiao[i]=get(a[i]-1);
da[i]=i-get(a[i]);
}
for(register int i=1;i<=n;++i) t[i]=0;
for(register int i=1;i<=m;++i){
q[i].l=read();q[i].r=read();
q[i].id=i;
}
sort(q+1,q+1+m,cmpq);
int l=1,r=0;
for(register int i=1;i<=m;++i){
//++r f(x,[l,r])=f(x,[1,r])-f(x,[1,l-1])
if(r<q[i].r) p[l-1].push_back((node){r+1,q[i].r,-1,q[i].id,2});
for(;r<q[i].r;++r) ans[q[i].id]+=da[r+1];
//--l
if(l>q[i].l) p[r].push_back((node){q[i].l,l-1,1,q[i].id,1});
for(;l>q[i].l;--l) ans[q[i].id]-=xiao[l-1];
//--r;
if(r>q[i].r) p[l-1].push_back((node){q[i].r+1,r,1,q[i].id,2});
for(;r>q[i].r;--r)ans[q[i].id]-=da[r];
//++l
if(l<q[i].l) p[r].push_back((node){l,q[i].l-1,-1,q[i].id,1});
for(;l<q[i].l;++l)ans[q[i].id]+=xiao[l];
}
int v,id;
for(register int i=1;i<=n;++i){
xiao[i]=(a[i]-1)/B+1;
da[i]=a[i]/B+1;
}
for(register int i=1;i<=n;++i){
v=(a[i]/B);
for(register int k=1;k<=v;++k) ++h[k];
v=v*B+1;
for(;v<=a[i];++v) ++t[v];
for(register int j=0;j<p[i].size();++j){
id=p[i][j].id;
if(p[i][j].op==1){
if(p[i][j].f==1) {
ans[id]+=(LL)i*(p[i][j].r-p[i][j].l+1);
for(register int o=p[i][j].l;o<=p[i][j].r;++o)
ans[id]-=t[a[o]]+h[xiao[o]];
}
else{
ans[id]-=(LL)i*(p[i][j].r-p[i][j].l+1);
for(register int o=p[i][j].l;o<=p[i][j].r;++o)
ans[id]+=t[a[o]]+h[xiao[o]];
}
}
else{
if(p[i][j].f==1) {
for(register int o=p[i][j].l;o<=p[i][j].r;++o)
ans[id]+=t[a[o]+1]+h[da[o]];
}
else{
for(register int o=p[i][j].l;o<=p[i][j].r;++o)
ans[id]-=h[da[o]]+t[a[o]+1];
}
}
}
}
for(register int i=1;i<=m;++i) ans[q[i].id]+=ans[q[i-1].id];
for(register int i=1;i<=m;++i){
print(ans[i]);puts("");
}
return 0;
}
Yuno loves sqrt technology III
链接
P5048 Yuno loves sqrt technology III
吐槽
我以前也写过区间众数,前面部分想法是相同的。。
但以前在处理两边 (O(n^{0.5})) 大小的小块时,是直接用线段树来算区间内出现次数的。。
这题这个把问题转化为是否存在出现 (ans+1) 次元素,就不需要真的算出一个元素的出现次数。。
感觉自己还是太菜了。。只能膜拜想出这种做法的神仙。。
实现上感觉这是三题里最不卡常的,一发就过了~
(Code)
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=5e5+10;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void print(int x){
if(x>9) print(x/10);
putchar(x%10+'0');
}
int n,m,B,cnt;
int a[N],b[N],g[N],c[N],L[N],R[N];
int find(int x){
int l=1,r=cnt,mid;
while(1){
mid=l+r>>1;
if(b[mid]==x) return mid;
else if(b[mid]<x) l=mid+1;
else r=mid-1;
}
}
int F[2010][2010];
int pos[N];
vector<int> v[N];
int main(){
n=read();m=read();B=sqrt(n);
for(int i=1;i<=n;++i) {
a[i]=read();
b[i]=a[i];
g[i]=(i+B-1)/B;
}
for(int i=1;i<=g[n];++i){
L[i]=(i-1)*B+1;
R[i]=min(i*B,n);
}
sort(b+1,b+1+n);
cnt=1;
for(register int i=2;i<=n;++i)
if(b[i]!=b[i-1]) b[++cnt]=b[i];
for(register int i=1;i<=n;++i) a[i]=find(a[i]);
int mx;
for(int i=1;i<g[n];++i){
mx=0;
for(int j=i;j<g[n];++j){
for(int k=L[j];k<=R[j];++k){
++c[a[k]];
if(c[a[k]]>mx) mx=c[a[k]];
}
F[i][j]=mx;
}
for(int j=i;j<g[n];++j)
for(int k=L[j];k<=R[j];++k)
--c[a[k]];
}
for(int i=1;i<=n;++i){
pos[i]=c[a[i]];
++c[a[i]];
v[a[i]].push_back(i);
}
int ans=0,l,r;
for(int tt=1;tt<=m;++tt){
l=read()^ans;r=read()^ans;
ans=F[g[l]+1][g[r]-1];
for(int i=l;i<=R[g[l]];++i){
for(;pos[i]+ans<v[a[i]].size();++ans)
if(v[a[i]][pos[i]+ans]>r) break;
}
for(int i=L[g[r]];i<=r;++i){
for(;pos[i]-ans>=0;++ans)
if(v[a[i]][pos[i]-ans]<l) break;
}
print(ans);puts("");
}
return 0;
}