交通
倒序预处理 到达每个路口的时间为绿灯第 \(0\) 秒时到终点的最短用时
转移就是找到后缀第一个区间和在模意义下属于 \([g,g+r)\) 的位置,这个部分可以使用动态开点线段树维护
查询和预处理的需求形式类似,不过查询时直接暴力跑过去了是怎么回事呢
Code Display
const int N=5e4+10;
int f[N],n,g,r,d[N],Q,cyc,s[N];
const int M=N*90;
struct Seg{
int Mn[M],ls[M],rs[M],tot,rt;
inline void upd(int pos,int v,int &p,int l=0,int r=g+r-1){
if(!p) p=++tot;
if(l==r) return Mn[p]=v,void();
int mid=(l+r)>>1;
if(pos<=mid) upd(pos,v,ls[p],l,mid);
else upd(pos,v,rs[p],mid+1,r);
Mn[p]=n+2;
if(ls[p]) ckmin(Mn[p],Mn[ls[p]]);
if(rs[p]) ckmin(Mn[p],Mn[rs[p]]);
return ;
}
inline int query(int st,int ed,int p,int l=0,int r=g+r-1){
if(!p) return n+2;
if(st<=l&&r<=ed) return Mn[p];
int mid=(l+r)>>1,res=n+2;
if(st<=mid) ckmin(res,query(st,ed,ls[p],l,mid));
if(ed>mid) ckmin(res,query(st,ed,rs[p],mid+1,r));
return res;
}
}T;
inline int ask(int L,int R){
L%=(g+r); R%=(g+r);
if(L<=R) return T.query(L,R,T.rt);
return min(T.query(L,g+r-1,T.rt),T.query(0,R,T.rt));
}
signed main(){
freopen("traffic.in","r",stdin); freopen("traffic.out","w",stdout);
n=read(); g=read(); r=read();
rep(i,1,n+1) d[i]=read(),cyc+=d[i]/(g+r),d[i]%=g+r;
s[1]=d[1];
for(int i=2;i<=n+1;++i) s[i]=d[i]+s[i-1];
f[n+1]=d[n+1];
T.upd(s[n]%(g+r),n+1,T.rt);
for(int i=n;i>=1;--i){
int j=ask(s[i-1]+g,s[i-1]+g+r-1);
if(j!=n+2){
int v=s[j-1]-s[i-1];
f[i]=v+f[j]+(g+r)-v%(g+r);
}else f[i]=s[n+1]-s[i-1];
T.upd(s[i-1]%(g+r),i,T.rt);
}
int Q=read();
while(Q--){
int t=d[1]+read();
for(int i=2;i<=n+1;++i){
if(t%(g+r)>=g){
t+=(g+r)-t%(g+r);
t+=f[i];
break;
}
t+=d[i];
}
print(t+cyc*(g+r));
}
return 0;
}
选拔
诈 骗 大 师
考虑对于每个询问分开处理:设 \(f_{x,j}\) 表示从 \(x\) 子树里面是否存在一个点的根链满足对应字符串是 \(Q[1..i]\),\(g_{x,j}\) 表示匹配后缀
转移显然可以使用 std::bitset
优化
对于所有询问可以统一处理,将字符串连到一起并添加特殊字符即可
一种可能的实现是记录每个字符在全部字符串中的出现位置,并将特殊字符设为 \('z'+1\),每个树上节点初始化 \(\rm bitset\) 时置为特殊字符对应的出现位置
同时全局维护一个长度为拼接串串长的 \(\rm bitset\),其对应字符串上字符的位置表示用上述前后缀的 \(\rm DP\) 值是否可以 \(\rm and\) 得到
Code Display
const int N=3e4+10;
bitset<N*2> f[N],g[N],ans,b[27];
string s,t[N];
int n,Q;
vector<pair<int,int> > G[N];
int tim,ord[N],fa[N];
inline void get_fa(int x,int fat){
ord[++tim]=x; fa[x]=fat;
f[x]=g[x]=b[26];
for(auto t:G[x]) if(t.fir!=fat) get_fa(t.fir,x);
return ;
}
signed main(){
freopen("selection.in","r",stdin); freopen("selection.out","w",stdout);
n=read();
for(int i=1;i<n;++i){
int u=read(),v=read(),k=Getalpha()-'a';
G[u].emplace_back(v,k);
G[v].emplace_back(u,k);
}
Q=read();
for(int i=1;i<=Q;++i){
cin>>t[i];
s=s+char('z'+1)+t[i];
}
s+=char('z'+1);
int len=s.length();
for(int i=0;i<len;++i) b[s[i]-'a'][i]=1;
get_fa(1,0);
for(int id=n;id>=1;--id){
int x=ord[id];
for(auto e:G[x]){
int t=e.fir; if(t==fa[x]) continue;
f[t]=(f[t]<<1)&b[e.sec];
g[t]=(g[t]>>1)&b[e.sec];
ans=ans|((f[x]<<1)&g[t]);
ans=ans|(g[x]&(f[t]<<1));
f[x]|=f[t]; g[x]|=g[t];
}
}
int cur=1;
for(int i=1;i<=Q;++i){
int tlen=t[i].size();
for(int j=0;j<=tlen;++j) if(ans[cur+j]){puts("YES"); goto Succ;}
puts("NO");
Succ:;
cur+=tlen+1;
}
return 0;
}
等待
尝试判断某个数字 \(x\) 是否能作为合法的 \(\sum b\),简记 \(a_1\ge a_2\dots\ge a_n\):
-
如果 \(x\) 的最高位比 \(a_{\max}\) 的最高位高那么删掉 \(a_{\max}\) 变成子问题
-
如果 \(x\) 的最高位和 \(a_{\max}\) 的最高位相同那么删掉 \(a_{\max}\) 的最高位得到新的 \(\{a\}\) 变成子问题
-
如果 \(x\) 的最高位比 \(a_{\max}\) 的最高位低那么不合法
不难发现这个做法没有后效性
显然可以在判定低位时保留高位对 \(a\) 的删除作用
具体实现可以使用 \(\text{radix sort}\) 来找到删掉若干个 \(1\) 之后的 \(a_{\max}\),基数排序过程就是维护前 \(i-1\) 位的排序结果并添加第 \(i\) 位,这位不同的直接由这位来决定大小关系
根据需求:对于最高位 \(<i\) 的数字不在处理第 \(i\) 位时考虑
这部分复杂度是 \(\Theta(\sum L)\) 的,懒得对 \(0/1\) 归并可以直接 std::sort
,问题不大
发现有些部分可以合并处理,也就是有一段 \(\sum b\) 上的连续位必须同时填 \(1\) ,考虑如下局面:
现在考虑到 \(\sum b\) 的第 \(p\) 位,仍然设 \(a_1\ge a_2\dots\ge a_n\),同时记 \(t_i\) 表示 \(a_i\) 的最高位,记 \(k=\min\{\arg \max\{i+t_i\}\}\)
如果 \(p>k+t_k\) 那么一定有解,因为每个数字都要比其最高位更高的 \(1\) 抵消之,同理若 \(p<k+t_k\) 那么一定没有解,据此可以加速判断
但是仍然有一个 \(p=k+t_k\) 的 \(\text{case}\) 需要处理,那么 \(p\leftarrow p-k,\) 并将 \(a_1\dots a_{k-1}\) 均置零并删掉 \(a_k\) 最高位并递归实现判定
递归次数是 \(\Theta(\sum L)\) 的,置零操作并不需要真的置零,只是需要查询的 \(\max\{i+t_i\}\) 的区间发生了变化而已
使用线段树维护 \(i+t_i\) 的最大值以及最靠左的出现位置,实现上有一个技巧是把 \(i+t_i\) 中的 \(i\) 拆成每个合法叶子上 \(1\) 的累加
Code Display
const int N=6e5+10;
int ones,mxl,n,len[N];
int head[N],nxt[N],id[N],tmp[N];
char ans[N];
string s[N];
bool vis[N];
struct node{
int mx,sum,pos; node(){mx=sum=pos=0;}
node(int a,int b,int c){mx=a; sum=b; pos=c;}
node operator +(const node &a)const{
node t; t.sum=sum+a.sum;
if(mx>=sum+a.mx) t.mx=mx,t.pos=pos;
else t.mx=sum+a.mx,t.pos=a.pos;
return t;
}
}t[N<<2];
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
inline void push_up(int p){ t[p]=t[ls]+t[rs];}
inline void build(int p,int l,int r){
if(l==r){
if(vis[l]) t[p]=node(len[l]+1,1,l);
return ;
} int mid=(l+r)>>1;
build(lson); build(rson);
return push_up(p);
}
inline void flip(int pos,int p=1,int l=1,int r=ones){
if(l==r){
if((vis[l]^=1)) t[p]=node(len[l]+1,1,l);
else t[p]=t[0];
return ;
}
int mid=(l+r)>>1;
if(pos<=mid) flip(pos,lson); else flip(pos,rson);
return push_up(p);
}
inline node query(int st,int ed,int p=1,int l=1,int r=ones){
if(st<=l&&r<=ed) return t[p];
int mid=(l+r)>>1; node ret=t[0];
if(st<=mid) ret=ret+query(st,ed,lson);
if(ed>mid) ret=ret+query(st,ed,rson);
return ret;
}
inline bool solve(int l,int h){
if(l>ones) return 1;
node cur=query(l,ones);
if(!cur.sum) return 1;
// All zero
if(cur.mx>h) return 0;
// highest bit illegal
if(nxt[cur.pos]) flip(nxt[cur.pos]);
bool legal=solve(cur.pos+1,len[cur.pos]);
if(nxt[cur.pos]) flip(nxt[cur.pos]);
if(legal){
for(int i=len[cur.pos];i<cur.mx;++i) ans[i+1]='1';
return 1;
}
if(cur.mx>=h) return 0;
//delete the highest number
solve(cur.pos+1,len[cur.pos]+1);
for(int i=len[cur.pos]+1;i<=cur.mx;++i) ans[i+1]='1';
return 1;
}
signed main(){
freopen("wait.in","r",stdin); freopen("wait.out","w",stdout);
n=read();
for(int i=1;i<=n;++i){
cin>>s[i];
int slen=s[i].length();
reverse(s[i].begin(),s[i].end());
for(int j=0;j<slen;++j) ones+=s[i][j]=='1';
ckmax(mxl,slen);
}
//radix sort
for(int i=1;i<=n;++i) id[i]=i;
int rem=n,pter=ones;
for(int l=0;l<mxl;++l){
int ord0=0,ord1=0;
for(int j=1;j<=rem;++j) ord1+=s[id[j]][l]=='0';
for(int j=1;j<=rem;++j){
if(s[id[j]][l]=='0') nxt[++ord0]=id[j];
else nxt[++ord1]=id[j];
}
rep(i,1,rem) id[i]=nxt[i];
for(int i=1;i<=rem;++i){
if(s[id[i]][l]=='1'){
nxt[pter]=head[id[i]];
head[id[i]]=pter;
len[pter--]=l;
}
}
int cnt=rem; rem=0;
for(int j=1;j<=cnt;++j){
if(s[id[j]].length()>l+1) id[++rem]=id[j];
}
}
for(int i=1;i<=n;++i) vis[head[i]]=1;
build(1,1,ones);
int Len=n+mxl;
rep(i,1,Len) ans[i]='0';
solve(1,Len);
while(Len>1&&ans[Len]!='1') --Len;
for(int i=Len;i>=1;--i) putchar(ans[i]); putchar('\n');
return 0;
}