【考试总结】2022-03-29
交通
倒序预处理 到达每个路口的时间为绿灯第 秒时到终点的最短用时
转移就是找到后缀第一个区间和在模意义下属于 的位置,这个部分可以使用动态开点线段树维护
查询和预处理的需求形式类似,不过查询时直接暴力跑过去了是怎么回事呢
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;
}
选拔
诈 骗 大 师
考虑对于每个询问分开处理:设 表示从 子树里面是否存在一个点的根链满足对应字符串是 , 表示匹配后缀
转移显然可以使用 std::bitset
优化
对于所有询问可以统一处理,将字符串连到一起并添加特殊字符即可
一种可能的实现是记录每个字符在全部字符串中的出现位置,并将特殊字符设为 ,每个树上节点初始化 时置为特殊字符对应的出现位置
同时全局维护一个长度为拼接串串长的 ,其对应字符串上字符的位置表示用上述前后缀的 值是否可以 得到
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;
}
等待
尝试判断某个数字 是否能作为合法的 ,简记 :
-
如果 的最高位比 的最高位高那么删掉 变成子问题
-
如果 的最高位和 的最高位相同那么删掉 的最高位得到新的 变成子问题
-
如果 的最高位比 的最高位低那么不合法
不难发现这个做法没有后效性
显然可以在判定低位时保留高位对 的删除作用
具体实现可以使用 来找到删掉若干个 之后的 ,基数排序过程就是维护前 位的排序结果并添加第 位,这位不同的直接由这位来决定大小关系
根据需求:对于最高位 的数字不在处理第 位时考虑
这部分复杂度是 的,懒得对 归并可以直接 std::sort
,问题不大
发现有些部分可以合并处理,也就是有一段 上的连续位必须同时填 ,考虑如下局面:
现在考虑到 的第 位,仍然设 ,同时记 表示 的最高位,记
如果 那么一定有解,因为每个数字都要比其最高位更高的 抵消之,同理若 那么一定没有解,据此可以加速判断
但是仍然有一个 的 需要处理,那么 并将 均置零并删掉 最高位并递归实现判定
递归次数是 的,置零操作并不需要真的置零,只是需要查询的 的区间发生了变化而已
使用线段树维护 的最大值以及最靠左的出现位置,实现上有一个技巧是把 中的 拆成每个合法叶子上 的累加
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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律