观察一下 .
我们可以贪心从 的左端点跳到前一个 的左端点,复杂度 .
#include<cstdio>
#include<algorithm>
#define mk make_pair
using namespace std;
const int N=5e5+10;
int n,id[N],pre[N],s[N],m;
char ch[N]; pair<int,int> ans;
int main() {
scanf("%d%s",&n,ch+1);
for(int i=1,x=0,y=0;i<=n;i++)
if(ch[i]=='0') pre[i]=y,x=i,s[++m]=i,id[i]=m;
else pre[i]=x,y=i;
ans=mk(n-m,0);
for(int i=1;i<=m;i++) {
int x=s[m-i+1],y=i;
while(id[pre[pre[x]]]>=i) {
y+=i+1;
x=s[id[pre[pre[x]]]-i+1];
}
if(y>i) ans=max(ans,mk(y,i));
else break;
}
int a=ans.first,b=ans.second;printf("%d\n",a);
while(a) {
for(int i=1;i<=b;i++) putchar('0');
a-=b; if(a) putchar('1'),a--;
}
return 0;
}
线段树.
我们在规定直线上跑最短路.
若两点都未被直线覆盖则不合法.
若一行一列覆盖两点,那么曼哈顿距离则为答案.
否则,都被行/列覆盖.我们下面只讲行的情况.
若中间有列或中间的行都被覆盖则为曼哈顿距离.
否则,往左/右走一段,再折回来.
#include<bits/stdc++.h>
#define gc getchar()
using namespace std;
const int N=1e6+10;
template<class o> void qr(o&x) {
char c=gc; x=0; int f=1;
while(!isdigit(c)) {if(c=='-') f=-1; c=gc;}
while(isdigit(c))x=x*10+c-'0',c=gc;
x*=f;
}
template<class o> void qw(o x) {
if(x<0) putchar('-'),x=-x;
if(x/10) qw(x/10);
putchar(x%10+'0');
}
template<class o> void pr2(o x) {qw(x); puts("");}
int n,m,q,now,row[N],col[N];
struct ST {
int mx[N<<2],mn[N<<2];
#define lc (x<<1)
#define rc (x<<1|1)
void add(int x,int l,int r,int pos) {
mx[x]=now;
if(l==r) {mn[x]=now; return ;}
int mid=(l+r)/2;
if(pos<=mid) add(lc,l,mid,pos);
else add(rc,mid+1,r,pos);
mn[x]=min(mn[lc],mn[rc]);
}
int Max(int x,int l,int r,int L,int R) {
if(L<=l&&r<=R) return mx[x];
int mid=(l+r)/2,t=0;
if(L<=mid) t=Max(lc,l,mid,L,R);
if(mid< R) t=max(t,Max(rc,mid+1,r,L,R));
return t;
}
int Min(int x,int l,int r,int L,int R) {
if(L<=l&&r<=R) return mn[x];
int mid=(l+r)/2,t=N;
if(L<=mid) t=Min(lc,l,mid,L,R);
if(mid< R) t=min(t,Min(rc,mid+1,r,L,R));
return t;
}
int solvel(int x,int l,int r,int p,int v) {
if(l>=p||mx[x]<v) return 0;
if(l==r) return l;
int mid=(l+r)/2,t=solvel(rc,mid+1,r,p,v);
if(!t) t=solvel(lc,l,mid,p,v);
return t;
}
int solver(int x,int l,int r,int p,int v) {
if(r<=p||mx[x]<v) return 0;
if(l==r) return l;
int mid=(l+r)/2,t=solver(lc,l,mid,p,v);
if(!t) t=solver(rc,mid+1,r,p,v);
return t;
}
} r,c;
int solve(int x1,int y1,int x2,int y2,int v) {
int a=(row[x1]>=v)|((col[y1]>=v)*2),b=(row[x2]>=v)|((col[y2]>=v)*2);
if(!a||!b) return -1;
if(x1>x2) swap(x1,x2);
if(y1>y2) swap(y1,y2);
int ans=x2-x1+y2-y1,tmp,ad=N;
if((a|b)==3) return ans;
if((a&b)==1) {
if(r.Min(1,1,n,x1,x2)>=v||c.Max(1,1,m,y1,y2)>=v) return ans;
if(tmp=c.solvel(1,1,m,y1,v)) ad=min(ad,y1-tmp);
if(tmp=c.solver(1,1,m,y2,v)) ad=min(ad,tmp-y2);
if(ad==N) return -1;
ans+=ad*2;
}
else {
if(c.Min(1,1,m,y1,y2)>=v||r.Max(1,1,n,x1,x2)>=v) return ans;
if(tmp=r.solvel(1,1,n,x1,v)) ad=min(ad,x1-tmp);
if(tmp=r.solver(1,1,n,x2,v)) ad=min(ad,tmp-x2);
if(ad==N) return -1;
ans+=ad*2;
}
return ans;
}
int main() {
qr(n); qr(m); qr(q);
for(int i=1,op,x1,y1,x2,y2,v;i<=q;i++) {
qr(op); qr(x1); now=i;
if(op==1) row[x1]=i,r.add(1,1,n,x1);
else if(op==2) col[x1]=i,c.add(1,1,m,x1);
else {
qr(y1); qr(x2); qr(y2); qr(v);
pr2(solve(x1,y1,x2,y2,i-v));
}
}
return 0;
}
一开始想了用树状数组求 .但是显然会T.
考虑优化,假设当前的所有点都未删去,我们求得的 值为 .
如果删去的数为 ,则答案为 .
所以用单调队列维护一下删除数即可.
#include<bits/stdc++.h>
#define lc (x<<1)
#define rc (x<<1|1)
#define gc getchar()//(p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#define mk make_pair
#define pi pair<int,int>
#define pb push_back
#define IT iterator
#define fi first
#define se second
#define vi vector<int>
#define SZ(a) ((int)a.size())
#define all(a) a.begin(),a.end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N=2e6+10,size=1<<20;
//char buf[size],*p1=buf,*p2=buf;
template<class o> void qr(o &x) {
char c=gc; x=0; int f=1;
while(!isdigit(c)){if(c=='-')f=-1; c=gc;}
while(isdigit(c)) x=x*10+c-'0',c=gc;
x*=f;
}
template<class o> void qw(o x) {
if(x/10) qw(x/10);
putchar(x%10+'0');
}
template<class o> void pr1(o x) {
if(x<0)x=-x,putchar('-');
qw(x); putchar(' ');
}
template<class o> void pr2(o x) {
if(x<0)x=-x,putchar('-');
qw(x); puts("");
}
namespace IO{
int c;
unsigned int seed;
unsigned int randnum(){
seed^=seed<<13;
seed^=seed>>17;
seed^=seed<<5;
return seed;
}
inline int read(int &x){scanf("%d",&x);return x;}
inline void init_case(int &m,int &a,int &b,int &d,int p[]){
scanf("%d%u%d%d%d%d",&m,&seed,&a,&b,&c,&d);
for(int i=1;i<=m;i++){
if(randnum()%c==0)p[i]=-1;
else p[i]=randnum()%b;
}
}
inline void update_ans(unsigned int &ans_sum,unsigned int cur_ans,int no){
const static unsigned int mod=998244353;
ans_sum^=(long long)no*(no+7)%mod*cur_ans%mod;
}
}
using IO::read;
using IO::init_case;
using IO::update_ans;
bool buy[N],vis[N];
int q[N],l,r;//丢失
int T,m,a,b,d;
int c[N],lg,cnt;
namespace Q {
int q[N],l,r;
void init(){l=1; r=0;}
void push(int x) {
while(l<=r&&q[r]>x) r--;
q[++r]=x;
}
void del(int x) {
if(l<=r&&q[l]==x) l++;
}
int front() {return l<=r?q[l]:N;}
}
int ask() {
while(buy[cnt+1]) cnt++;
return min(cnt,Q::front());
}
int main(){
// freopen("c.in","r",stdin);
// freopen("c.out","w",stdout);
static int p[2000005];
read(T);
while(T--) {
Q::init();
unsigned int ans_sum=0,cur_ans=0;
memset(c+1,0,sizeof(int[b]));
memset(buy+1,0,sizeof(bool[b+1]));
memset(vis+1,0,sizeof(bool[b+1]));//树状数组无0,所以整体+1
init_case(m,a,b,d,p); b++; cnt=a+1;
for(lg=1;(1<<(lg+1))<=b;lg++);
for(int i=0;i<=a;i++)
buy[i+1]=vis[i+1]=1;
l=1; r=0;
for(int i=1;i<=m;i++) {
cur_ans=0;
int x=p[i];
if(x==-1) {
if(!d&&l<=r) {
x=q[l++];
Q::del(x-1);
vis[x]=1;
cur_ans=ask();
}
}
else {
++x;
if(!buy[x]) {
buy[x]=vis[x]=1;
cur_ans=ask();
}
else if(!d) {
if(vis[x]) {
vis[x]=0;
q[++r]=x;
Q::push(x-1);
cur_ans=ask();
}
else if(l<=r) {
x=q[l++];
vis[x]=1;
Q::del(x-1);
cur_ans=ask();
}
}
}
update_ans(ans_sum,cur_ans,i);
}
printf("%u\n",ans_sum);
}
return 0;
}