省选模拟3.9
A. 鱼死网破
看给出的部分分,给出的纵坐标都相同
于是从所有鱼头胖往每个墙的两个端点分别引一条线
覆盖到胖头鱼所在的纵坐标线,可以差分以下,左边的线 \(-1\) 右边的 \(+1\)
可能会有重复覆盖的情况
于是可以用类似括号匹配的方法对每个鱼头胖引出的线进行处理
可以扩展这个做法,只需要将所有线都存到墙的端点里,再按照极角序排序
然后再跟胖头鱼的连线比较二分一下,就能知道要减或者加多少个 \(1\) 了
可以用叉积来进行极角序排序
Code
#include<bits/stdc++.h>
//#define int long long//OVERFLOW !!! MEMORY LIMIT !!!
#define rint signed
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline 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<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,k,m,op;
int x[100010],y[100010];
int l[60],r[60],h[60];
struct data{int x,y;inline bool operator<(const data &b)const{return 1ll*(1ll*x*b.y-1ll*y*b.x)>0ll;}};
struct dddd{int x,y,k,l;inline bool operator<(const dddd &b)const{return 1ll*(1ll*x*b.y-1ll*y*b.x)>0ll;}};
vector<data>vec[110];
vector<dddd>v;
signed main(){
#ifdef LOCAL
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
freopen("clash.in","r",stdin);
freopen("clash.out","w",stdout);
n=read(),k=read(),m=read(),op=read();
for(int i=1;i<=n;i++) x[i]=read(),y[i]=read();
for(int i=1;i<=k;i++) l[i]=read(),r[i]=read(),h[i]=read();
for(int i=1;i<=n;i++){
v.clear();
for(int j=1;j<=k;j++) if(y[i]>h[j]){
v.emplace_back((dddd){l[j]-x[i],h[j]-y[i],-1,j});
v.emplace_back((dddd){r[j]-x[i],h[j]-y[i], 1,j});
}
sort(v.begin(),v.end());
for(int j=0,L=0,R=0,k=0;j<v.size();j++){
if(v[j].k==-1) k--;
if(v[j].k== 1) k++;
if(k==-1&&v[j].k==-1) L=j;
if(k== 0&&v[j].k== 1){
R=j;
vec[v[L].l*2 ].emplace_back((data){v[L].x,v[L].y});
vec[v[R].l*2+1].emplace_back((data){v[R].x,v[R].y});
}
}
}
for(int i=2;i<=k*2+1;i++) sort(vec[i].begin(),vec[i].end());
for(int i=1,x,y,res=0;i<=m;i++){
x=read()^(res*op),y=read()^(res*op);res=n;
for(int j=1,vv;j<=k;j++){
vv=upper_bound(vec[j*2 ].begin(),vec[j*2 ].end(),(data){x-l[j],y-h[j]})-vec[j*2 ].begin();res-=vv;
vv=lower_bound(vec[j*2+1].begin(),vec[j*2+1].end(),(data){x-r[j],y-h[j]})-vec[j*2+1].begin();res+=vv;
}
printf("%d\n",res);
}
return 0;
}
B. 漏网之鱼
正着添加一个数,修改的位置总共 \(O(n)\) 个,可以根据这个做,我不太会
于是考虑倒着删除一个数
那修改的区间肯定是他上一次出现的位置一直往前直到 \(mex = a_i\)
可以用线段树上二分找到这个位置
那现在就要维护区间赋值和区间历史值和
线段树上每个节点维护两个信息,一个是当前的 \(mex\) 一个是 \(sum\)
\(sum\) 表示的就是以当前位置为左端点,右端点在现在枚举的位置到 \(n\) 的区间的 \(mex\) 值和
用类似扫描线的思路把询问离线下来
拆成 \(l\) 和 \(r+1\) 分别询问 \([l,r]\) 的值然后造成 \(+1,-1\) 的贡献
具体维护,我用了 \(3\) 个标记
分别是 \(c,u,v\) 分别表示累加次数,赋值标记,在赋值标记后的累加贡献
下传时先 \(c\) 再 \(u\) , \(v\) 无所谓
下传 \(c\) 时要先判断左右儿子有没有 \(u\) ,有的话就转成 \(v\) 没有就正常下传
Code
#include<bits/stdc++.h>
#define int long long//OVERFLOW !!! MEMORY LIMIT !!!
#define rint signed
#define lson rt<<1
#define rson rt<<1|1
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline 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<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,q,blo;
int a[1000010],pre[1000010],lst[1000010];
bool vis[1000010];
int ans[1000010],mex[1000010];
int bl[1000010],v[1000010];
int L[1010],R[1010];
int sum[1010],vsum[1010],utag[1010],ctag[1010],vtag[1010],mx[1010];
struct data{int l,r,k,id;};
vector<data>vec[1000010];
struct seg{
int mx;
int sum,vsum;
int utag,ctag,vtag;
}st[1000010*4];
inline void pushup(int rt){
st[rt].mx=max(st[lson].mx,st[rson].mx);
st[rt].sum=st[lson].sum+st[rson].sum;
st[rt].vsum=st[lson].vsum+st[rson].vsum;
}
inline void pushdown(int rt,int l,int r){
int mid=(l+r)>>1;
if(st[rt].ctag){
st[lson].vsum+=st[lson].sum*st[rt].ctag;
st[rson].vsum+=st[rson].sum*st[rt].ctag;
if(st[lson].utag!=-1) st[lson].vtag+=st[lson].utag*st[rt].ctag;else st[lson].ctag+=st[rt].ctag;
if(st[rson].utag!=-1) st[rson].vtag+=st[rson].utag*st[rt].ctag;else st[rson].ctag+=st[rt].ctag;
st[rt].ctag=0;
}
if(st[rt].utag!=-1){
st[lson].utag=st[rson].utag=st[rt].utag;
st[lson].mx=st[rson].mx=st[rt].utag;
st[lson].sum=st[rt].utag*(mid-l+1);
st[rson].sum=st[rt].utag*(r-mid);
st[rt].utag=-1;
}
if(st[rt].vtag){
st[lson].vsum+=st[rt].vtag*(mid-l+1);
st[rson].vsum+=st[rt].vtag*(r-mid);
st[lson].vtag+=st[rt].vtag;
st[rson].vtag+=st[rt].vtag;
st[rt].vtag=0;
}
}
void build(int rt,int l,int r){
st[rt].utag=-1;if(l==r) return st[rt].mx=st[rt].sum=st[rt].vsum=mex[l],void();
int mid=(l+r)>>1;
build(lson,l,mid);
build(rson,mid+1,r);
pushup(rt);
}
void upd(int rt,int l,int r,int L,int R,int k){
if(L<=l&&r<=R) return st[rt].mx=st[rt].utag=k,st[rt].sum=(r-l+1)*k,void();
int mid=(l+r)>>1;pushdown(rt,l,r);
if(L<=mid) upd(lson,l,mid,L,R,k);
if(R>mid) upd(rson,mid+1,r,L,R,k);
pushup(rt);
}
int query(int rt,int l,int r,int L,int R){
if(L<=l&&r<=R) return st[rt].vsum;
int mid=(l+r)>>1,res=0;pushdown(rt,l,r);
if(L<=mid) res+=query(lson,l,mid,L,R);
if(R>mid) res+=query(rson,mid+1,r,L,R);
pushup(rt);return res;
}
int getpos(int rt,int l,int r,int k){
if(st[rt].mx<k) return -1;if(l==r) return l;
int mid=(l+r)>>1;pushdown(rt,l,r);
if(st[rson].mx>k) return getpos(rson,mid+1,r,k);
else return getpos(lson,l,mid,k);
}
signed main(){
#ifdef LOCAL
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
freopen("escape.in","r",stdin);
freopen("escape.out","w",stdout);
read();n=read();for(int i=1;i<=n;i++) a[i]=read();blo=sqrt(n);
for(int i=1;i<=n;i++) if(a[i]<=n){pre[i]=lst[a[i]];lst[a[i]]=i;}
for(int i=n,v=0;i;i--){
if(a[i]<=n) vis[a[i]]=1;
while(vis[v]) v++;mex[i]=v;
}
build(1,1,n);q=read();
for(int i=1,l,r;i<=q;i++){
l=read(),r=read();
vec[l].emplace_back((data){l,r,1,i});
vec[r+1].emplace_back((data){l,r,-1,i});
}
for(int i=n,l,r;i;i--){
for(auto L:vec[i]) ans[L.id]+=L.k*query(1,1,n,L.l,L.r);
if(a[i]<=n){
l=pre[i]+1,r=getpos(1,1,n,a[i]);
if(l<=r) upd(1,1,n,l,r,a[i]);
}
if(st[1].utag!=-1) st[1].vtag+=st[1].utag,st[1].vsum+=st[1].utag*n;
else st[1].ctag++,st[1].vsum+=st[1].sum;
}
for(int i=1;i<=q;i++) printf("%lld\n",ans[i]);
return 0;
}
C. 浑水摸鱼
转化一下,就是求本质不同子串个数,一般用 \(SA\) 或者 \(SAM\)
由于最小表示法,所以用 \(SA\) 来求
比较时,先二分求出 \(lcp\) 再比较下一位的值
将字符串的哈希定义为每个字符与他前一个相同的字符相差的距离
用主席树存下每一个后缀的哈希值
排完序后直接求本质不同子串个数
Code
#include<bits/stdc++.h>
//#define int long long//OVERFLOW !!! MEMORY LIMIT !!!
#define rint signed
#define cmin(x,y) ((x)<(y))?(x):(y)
#define uint unsigned long long
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline 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<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,ans;
int a[50010],t[50010];
int rt[50010],lst[50010],tot;
int pre[50010],nxt[50010],v[50010];
uint pw[50010];
struct seg{int ls,rs;uint hs;}st[50010*20];
inline void pushup(int x,int l,int r){int mid=(l+r)>>1;st[x].hs=st[st[x].ls].hs+st[st[x].rs].hs*pw[mid-l+1];}
void build(int &x,int l,int r){
x=++tot;if(l==r) return st[x].hs=v[l],void();
int mid=(l+r)>>1;
build(st[x].ls,l,mid);
build(st[x].rs,mid+1,r);
pushup(x,l,r);
}
void upd(int &x,int l,int r,int pos,uint k){
int pre=x;x=++tot;st[x]=st[pre];if(l==r) return st[x].hs=k,void();
int mid=(l+r)>>1;
if(pos<=mid) upd(st[x].ls,l,mid,pos,k);
else upd(st[x].rs,mid+1,r,pos,k);
pushup(x,l,r);
}
uint query(int x,int l,int r,int L,int R){
if(L<=l&&r<=R) return st[x].hs;int mid=(l+r)>>1;
if(R<=mid) return query(st[x].ls,l,mid,L,R);
if(L>mid) return query(st[x].rs,mid+1,r,L,R);
return query(st[x].ls,l,mid,L,R)+query(st[x].rs,mid+1,r,L,R)*pw[cmin(mid-L+1,mid-l+1)];
}
inline int getlcp(int x,int y){
int lenx=n-x+1,leny=n-y+1;
int l=1,r=cmin(lenx,leny),res=0;
while(l<=r){
int mid=(l+r)>>1;
if(query(rt[x],1,n,x,x+mid-1)==query(rt[y],1,n,y,y+mid-1)) l=mid+1,res=mid;
else r=mid-1;
}
return res;
}
inline int s(int x,int k){return (pre[k]>=x)?k-pre[k]+1:1;}
inline bool cmp(int x,int y){
int lenx=n-x+1,leny=n-y+1;
int l=1,r=cmin(lenx,leny),res=0;
while(l<=r){
int mid=(l+r)>>1;
if(query(rt[x],1,n,x,x+mid-1)==query(rt[y],1,n,y,y+mid-1)) l=mid+1,res=mid;
else r=mid-1;
}
if(res==min(lenx,leny)) return lenx<leny;
return s(x,x+res)<s(y,y+res);
}
signed main(){
#ifdef LOCAL
freopen("in","r",stdin);
freopen("out","w",stdout);
#endif
freopen("waterflow.in","r",stdin);
freopen("waterflow.out","w",stdout);
pw[0]=1;for(int i=1;i<=50000;i++) pw[i]=pw[i-1]*131;
n=read();for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++){nxt[pre[i]=lst[a[i]]]=i;lst[a[i]]=i;}
for(int i=1;i<=n;i++){
if(pre[i]) v[i]=i-pre[i]+1;else v[i]=1;
}
build(rt[1],1,n);
for(int i=2;i<=n;i++){
rt[i]=rt[i-1];
if(nxt[i-1]) upd(rt[i],1,n,nxt[i-1],1);
}
for(int i=1;i<=n;i++) t[i]=i;
stable_sort(t+1,t+1+n,cmp);ans=n-t[1]+1;
for(int i=2;i<=n;i++){ans+=n-t[i]+1;ans-=getlcp(t[i-1],t[i]);}
printf("%d\n",ans);
return 0;
}