《LGJOJ 8.25》 测试总结
纯菜了,属于是。
中间还咕了很多场总结。。。
\(T1\) 简单游戏



输入:

输出:

\(\color{red}analysis:\)
考试的时候看错题了,寄。
正常做就是直接暴力区间 \(dp\) 就好了
就是正常的博弈论 \(dp\)
其他没什么好说的了,时间复杂度 \(O(n^2)\)
\(PS:\) 挂成了 \(30pts\)
\(PS:\) 没加记忆化都过了, \(gj\) 的数据不做评价。
点击查看代码
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int MAXN=2010;
int T,n;
int f[MAXN][MAXN];
char st[MAXN];
int pd(int x,int y) {
if(x<y) return 1;
if(x==y) return 0;
if(x>y) return 2;
}
void dfs(int opt,int l,int r,int cc) {
if(l>r) return ;
if(f[l][r]!=-1) return ;
if(!opt) {
dfs(opt^1,l+1,r,st[l]);
int u=f[l+1][r];
dfs(opt^1,l,r-1,st[r]);
int v=f[l][r-1];
if(u==1||v==1) f[l][r]=1;
else if(!u||!v) f[l][r]=0;
else f[l][r]=2;
}
else {
if(st[l]<cc||st[r]<cc) {
f[l][r]=2;
return ;
}
else if(st[l]==cc||st[r]==cc) {
if(l==r) {
f[l][r]=0;
return ;
}
int u=1,v=1;
if(st[l]==cc) {
dfs(opt^1,l+1,r,cc);
u=f[l+1][r];
}
if(st[r]==cc) {
dfs(opt^1,l,r-1,cc);
v=f[l][r-1];
}
if(u==2||v==2) f[l][r]=2;
else if(!u||!v) f[l][r]=0;
else f[l][r]=1;
}
else {
f[l][r]=1;
}
}
}
int main () {
scanf("%d",&T);
while(T--) {
scanf("%s",st+1);
n=strlen(st+1);
for(int i=1;i<=n;++i) {
for(int j=1;j<=n;++j) {
f[i][j]=-1;
}
}
dfs(0,1,n,0);
if(f[1][n]==0) puts("Draw");
if(f[1][n]==1) puts("Alice");
if(f[1][n]==2) puts("Bob");
}
return 0;
}
\(T2\) 内鬼





输入:

输出:
2
\(\color{blue}analysis:\)
首先我们考虑暴力 \(f_{i,j,S}\) 表示两个内鬼分别在 \(i,j\) 位置,抓到 \(S\) 这个集合里人所耗费的最少时间,然后我们可以获得一个 \(O(en^22^8)\) 的优秀解法,可以获得 \(30pts\)
\(if(dis[i][t]\le e[i].time-f_{i,j,S})\) 如果满足这个条件,我们的 \(dp\) 就可以转移,所以我们需要预处理
但是这样还不够,由于 \(t3\) 便秘时间过长,耗费了将近 \(1h\) 没想到怎么做,于是就回来写这题,突然想到两个内鬼可以拆开计算,然后枚举两个内鬼分别抓捕的人的集合,分别拆成 \(f_{x,S'},f_{y,S''}\) 。
然后具体能不能做到 \(50pts\) 那一档部分分呢,我觉得应该是可以搞到 \(en2^8\) 的,然后拿 \(50pts\)。
然后由于如果我们想拿 \(100pts\) ,我们就绝对不能预处理 \(dis\) 数组,我们考虑有没有一种方法,不用预处理 \(dis\) 数组。
这时候宋哥跑过来和我说分层图,然后就恍然大悟了属于是,我们既然不能直接预处理,那么我们就考虑直接跑最短路,然后对于每个不同的集合 \(S\) ,分成一层,至多分成 \(256\) 层,然后对于每层跑一下最短路,对于那些跨越层之间的路径也是照样跑就行了。
时间复杂度 \(O((2^km+2^ke)\log (2^kn))\) 空间复杂度也比较大,一种比较好的优化方法就是先对每层跑好在去跑分层图的边,这样就不用存那么多边的信息。
点击查看代码
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int MAXN=2e4+10,NN=5200000,MM=2560010,inf=1e9;
int T,n,m,k,Q,S;
struct daduoli {
int f,t,c;
}que[MAXN];
int id(int opt,int x) {
return opt*n+x;
}
struct ddl {
int t,c;
bool pp;
};
vector<ddl> e[NN];
void add(int f,int t,int c,int pp) {
e[f].push_back((ddl){t,c,pp});
e[t].push_back((ddl){f,c,pp});
}
void adline(int f,int t,int c,int pp) {
add(f,t,c,pp);
add(t,f,c,pp);
}
int dis[MM],sf[MM],res,X[260],Y[260];
void dij(int sta) {
int R=id((1<<k)-1,n);
for(int i=1;i<=R;++i) {
dis[i]=inf;
sf[i]=0;
}
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
q.push(make_pair(0,sta));
dis[sta]=0;
while(!q.empty()) {
int u=q.top().second;
q.pop();
if(sf[u]) continue;
sf[u]=1;
for(auto t:e[u]) {
if(!t.pp) {
if(dis[u]+t.c<dis[t.t]) {
dis[t.t]=dis[u]+t.c;
q.push(make_pair(dis[t.t],t.t));
}
}
else {
if(dis[u]<=t.c) {
if(t.c<dis[t.t]) {
dis[t.t]=t.c;
q.push(make_pair(dis[t.t],t.t));
}
}
}
}
}
for(int i=0;i<S;++i) {
if(!res) X[i]=inf;
else Y[i]=inf;
for(int j=1;j<=n;++j) {
if(!res) {
X[i]=min(X[i],dis[id(i,j)]);
}
else {
Y[i]=min(Y[i],dis[id(i,j)]);
}
}
}
}
int main () {
scanf("%d",&T);
while(T--) {
scanf("%d%d%d",&n,&m,&k);
S=(1<<k);
for(int i=1;i<=m;++i) {
scanf("%d%d%d",&que[i].f,&que[i].t,&que[i].c);
}
for(int i=0;i<S;++i) {
for(int j=1;j<=m;++j) {
adline(id(i,que[j].f),id(i,que[j].t),que[j].c,0);
}
}
scanf("%d",&Q);
for(int i=1;i<=Q;++i) {
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
for(int j=0;j<S;++j) {
if(((j>>(x-1))&1)) continue;
add(id(j,y),id((j|(1<<(x-1))),y),w,1);
}
}
int x,y;
scanf("%d%d",&x,&y);
res=0;
dij(id(0,x));
res=1;
dij(id(0,y));
int ans=inf;
--S;
for(int i=0;i<=S;++i) {
if(X[i]==inf||Y[(S^i)]==inf) continue;
ans=min(ans,max(X[i],Y[(S^i)]));
}
if(ans!=inf) printf("%d\n",ans);
else printf("-1\n");
//init
for(int i=0;i<=S;++i) {
for(int j=1;j<=n;++j) {
e[id(i,j)].clear();
}
}
}
return 0;
}
\(T3\) 序列统计




\(analysis:\)
考试便秘想不出来。
首先对于 \(mex\) 有一个比较经典的操作,就是将每种数都分开来处理,从 \(1\sim m\) 来进行处理。
然后题目问最小的包含 \(1\sim m\) 的最小长度是多少,其实就是问 \(mex=i(i\in(2\sim m+1))\) 的最小的长度
\(dp\ +\) 主席树
\(\%\%\% sktn0089\ wzr\) 大神
由于我们的暴力是枚举一个左端点一直向右扫,我们考虑优化。
记 \(f_{i,0/1}\) 表示以 \(i\) 为左端点或右端点使得一段序列包含 \(1\sim a_i\) 的最小的序列长度是多少。
然后我们考虑从 \(a_i=1\) 的 \(i\) 开始枚举。
我们只对 \(f_{i,0}\) 讨论,也就是 \(i\) 为左端点进行讨论,因为为右端点也是同理即可。
我们查询 \(mex(a_i,a_{i+1}...a_{i+f_{i,0}-1})\) ,记为 \(v\) ,然后这个区间就可以贡献给 \(ans_{v}\) ,记 \(ans_v\) 为,\(mex\) 为 \(v\) 的最小序列长度。
对于如何求区间 \(mex\) 我们只需要用主席树查询 \(i+f_{i,0}-1\) 这个版本中最小的最后一次被染色的节点在 \(l\) 左边的即可,具体代码会解释。
然后我们找到这段区间右边第一个 \(a[j]=v\) 的,记为 \(R\) ,左边同理,记为 \(L\)

然后我们就更新这些对应的 \(f\) 即可。
不过注意最后的 \(ans\) 要做一个后缀最小值。
因为对于 \(1\ 3\ 2\) ,实际上找不到 \(mex=2\) 的序列,只找得到 \(mex=3\) 的序列,所以 \(ans_3\) 要贡献给 \(ans_2\) ,也就是后缀最小值。
时间复杂度 \(O(n\log n)\) 空间复杂度 \(n\log n\)
点击查看代码
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int MAXN=5e5+10,inf=1e9;
int n,m;
int a[MAXN],f[MAXN][2],ans[MAXN];
vector<int> e[MAXN];
int T[MAXN],tot;
struct daduoli {
int v,l,r;
}tr[MAXN*20];
int update(int pre,int l,int r,int x,int i) {
int nw=++tot;
tr[nw]=tr[pre];
if(l<r) {
int mid=(l+r)/2;
if(x<=mid) tr[nw].l=update(tr[pre].l,l,mid,x,i);
else tr[nw].r=update(tr[pre].r,mid+1,r,x,i);
tr[nw].v=min(tr[tr[nw].l].v,tr[tr[nw].r].v);//对于区间中最后出现位置的数找到一个最小值
}
else {
tr[nw].v=i;//更新x最后出现位置
}
return nw;
}
int query(int nw,int pre,int l,int r) {
if(tr[nw].v>=pre) return r+1;
if(l==r) return l;
int mid=(l+r)/2;
if(tr[tr[nw].l].v>=pre) return query(tr[nw].r,pre,mid+1,r);//如果左区间中所有数最后出现位置都在左端点右边,那就找右区间,因为左区间全都合法
return query(tr[nw].l,pre,l,mid);
}
int main () {
scanf("%d%d",&n,&m);
for(int i=1;i<=m+1;++i) ans[i]=inf;
for(int i=1;i<=n;++i) {
scanf("%d",&a[i]);
e[a[i]].push_back(i);
if(a[i]==1) f[i][0]=f[i][1]=1;
else f[i][0]=f[i][1]=inf;
T[i]=update(T[i-1],1,m,a[i],i);
}
for(int i=1;i<=m;++i) {
for(auto t:e[i]) {
if(f[t][0]<inf) {
int l=t,r=t+f[t][0]-1;
int v=query(T[r],l,1,m);
ans[v]=min(ans[v],f[t][0]);
auto it=lower_bound(e[v].begin(),e[v].end(),l)-e[v].begin();
if(it<e[v].size()) f[e[v][it]][1]=min(f[e[v][it]][1],e[v][it]-l+1);
--it;
if(it>=0) f[e[v][it]][0]=min(f[e[v][it]][0],r-e[v][it]+1);
}
if(f[t][1]<inf) {
int l=t-f[t][1]+1,r=t;
int v=query(T[r],l,1,m);
ans[v]=min(ans[v],f[t][1]);
auto it=lower_bound(e[v].begin(),e[v].end(),l)-e[v].begin();
if(it<e[v].size()) f[e[v][it]][1]=min(f[e[v][it]][1],e[v][it]-l+1);
--it;
if(it>=0) f[e[v][it]][0]=min(f[e[v][it]][0],r-e[v][it]+1);
}
}
}
for(int i=m;i>=2;--i) {
ans[i]=min(ans[i],ans[i+1]);//记得取一下后缀最小值
}
for(int i=2;i<=m+1;++i) {
printf("%d\n",ans[i]);
}
return 0;
}
线段树
听 \(forgiven\) 讲了一下。
大概是我们记录一个数组 \(f_{i,j}\) 表示从 \(i\) 开始,要使得区间 \(mex\) 为 \(j\) 的最小右端点为多少。
假如我们有这样一个序列,有 \(5\) 个数,那么我们有 \(f[1\sim 3][1]=3,f[4\sim 5][1]=5\)


然后此时我们添加了 \(2\) 之后,我们就要 对每个 \(2\) 进行分段,然后令区间取一个 \(max\) 。
如何计算贡献呢,对于 \(i\) 来说,贡献其实就是 \(f_{i,j}-i+1\)
我们进行区间取 \(max\) 的时候,我们可以发现 \(f\) 一定是单调递增的,所以我们二分区间中一个中间的点,满足他左边的数都是小于我们区间 \(max\) 的操作值,这样我们区间赋值,就变成了区间推平,而推平之后贡献,一定是区间右端点的贡献最优,我们修改的时候直接用区间右端点计算贡献,然后 \(push\_up\) 上去就好了
时间复杂度 \(O(nlogn)\)
\(T4\) 最短路




套路题了,属于是。
这题分成两步,首先我们如何维护边权,另外我们如何处理连边。
维护边权
主要解决方法是使用主席树 \(+\) 哈希,这样我们就可以在 \(log|V|\) 的时间复杂度里处理出答案,而对于 \(a\) 如何处理,我们就把他拆成 \(\log a\) 位,这样时间复杂度再乘上一个 \(log\) ,不过问题也不大,也就只是两只 \(\log\) 而已。
处理连边
很显然的是可以使用线段树优化建图的方法,但是这样我们就出现了 \(m\log n\) 条边,我们的时间复杂度就会去到 \(log^3\) ,我们考虑一个经典的 \(trick\) ,直接从 \(dij\) 的原理进行优化,就是说一个区间 \(l_1\sim r_1\) 向 \(l_2\sim r_2\) 连边,那么我们一定是选择 \(l_1\sim r_1\) 中最小的一个点来更新,所以我们只需要对第一个入栈的点进行松弛就可以了。
具体实现建一棵入树,不断往父亲节点爬,如果走到当前节点,就把节点上的边给松弛掉,然后 \(clear\) 掉就好了,但是我们还得维护区间取 \(min\)
也就是说我们需要手写一个堆,它能实现以下两个操作:
-
区间赋值
-
区间取 \(min\) 。
然后我们的时间复杂度就可以控制到 \(log^2\) ,但是这时候我们的空间复杂度来到了 \(log^2\)
因为如果将每个 \(a\) 都拆掉,那么会多一个 \(log\)
解决方案是考虑到 \(a\) 是连续的一段,且不会超过 \(loga\) 位,所以其实只会对很小一段的值产生影响,所以我们只需要在 \(loga\) 次修改中不保留中间修改的版本,实际上与只修改一次所增加的节点数是多不了多少的,可以看成 \(log\) 。
然后这题我们就做完了,但是我调不过去,懒得调了,具体实现差不多就那样。
时间复杂度 \(O(mlogalog|V|)\) 空间复杂度 \(O(|V|log|V|)\)
下面的代码慎重阅读,不知道为什么回收了还是会爆内存,然后还有一些点 \(WA\) ,懒得调了。
点击查看代码
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int MAXN=1e5+100,N=1e5+60,MODD=1e9+7,NN=MAXN*150;
int n,m,S,T,tot;
int pw[N+100];
struct dll {
int l2,r2,a,b,i;
};
int res;
int sf[MAXN],rt[MAXN];
struct trr {
int lc[NN],rc[NN],sum[NN],sz[NN];
LL hs[NN];
bool pp[NN];
int bin[NN],cnt;
void zt(int x,int v) {
lc[x]=lc[v]; sum[x]=sum[v];
rc[x]=rc[v]; sz[x]=sz[v]; hs[x]=hs[v];
}
void psup(int x) {
sum[x]=sum[lc[x]]+sum[rc[x]];
hs[x]=((LL)pw[sz[lc[x]]]*hs[rc[x]]%MODD+hs[lc[x]])%MODD;
sz[x]=sz[lc[x]]+sz[rc[x]];
}
void build(int &x,int l,int r) {
if(!cnt) x=++tot;
else x=bin[cnt--];
sz[x]=r-l+1; hs[x]=0;
if(l==r) return ;
int mid=(l+r)/2;
build(lc[x],l,mid);
build(rc[x],mid+1,r);
psup(x);
}
void insert(int &x,int u,int l,int r,int w) {
if(!cnt) x=++tot;
else {
x=bin[cnt--];
pp[x]=0;
}
zt(x,u);
if(l==r) {
++hs[x];
++sum[x];
return ;
}
int mid=(l+r)/2;
if(w<=mid) insert(lc[x],lc[u],l,mid,w);
if(w>mid) insert(rc[x],rc[u],mid+1,r,w);
psup(x);
}
void update(int &x,int u,int ori,int l,int r,int L,int R) {
if(l>=L&&r<=R) {
x=ori;
return ;
}
if(!cnt) x=++tot;
else x=bin[cnt--],pp[x]=0;
zt(x,u);
int mid=(l+r)/2;
if(L<=mid) update(lc[x],lc[u],lc[ori],l,mid,L,R);
if(R>mid) update(rc[x],rc[u],rc[ori],mid+1,r,L,R);
psup(x);
}
int query(int x,int l,int r,int L,int R) {
if(l>R||r<L) return 0;
if(l>=L&&r<=R) return sum[x];
int mid=(l+r)/2;
return (query(lc[x],l,mid,L,R)+query(rc[x],mid+1,r,L,R));
}
int QUERY(int x,int l,int r,int p,int num) {
if(l==r) return l;
int mid=(l+r)/2;
if(sum[lc[x]]>=mid-p+1+num) return QUERY(rc[x],mid+1,r,p,num-sum[lc[x]]);
return QUERY(lc[x],l,mid,p,num);
}
void cop(int &x,int y,int l,int r) {
if(x==y) return ;
int ux=x;
if(!cnt) x=++tot;
else x=bin[cnt--],pp[x]=0;
zt(x,ux);
if(l==r) {
sz[x]=sz[y]; hs[x]=hs[y]; sum[x]=sum[y];
return ;
}
int mid=(l+r)/2;
cop(lc[x],lc[y],l,mid);
cop(rc[x],rc[y],mid+1,r);
sz[x]=sz[y]; hs[x]=hs[y]; sum[x]=sum[y];
psup(x);
}
void mdf(int &x,int lst,int w,int st) {
int asd=tot,y=lst;
while(w) {
if(w&1) {
int c=(st>0?query(lst,0,N,0,st-1):0);
int y=QUERY(lst,0,N,st,c);
if(y>st) update(x,lst,rt[0],0,N,st,y-1);
else x=lst;
insert(x,x,0,N,y);
lst=x;
}
++st;
w/=2;
}
int R=tot;
cop(y,x,0,N); x=y;
for(int j=asd+1;j<=R;++j) {
bin[++cnt]=j;
sz[j]=0;
}
}
bool cmp(int a,int b,int l,int r) {
if(l==r) return sum[a]>sum[b];
int mid=(l+r)/2;
if(hs[rc[a]]==hs[rc[b]]) return cmp(lc[a],lc[b],l,mid);
return cmp(rc[a],rc[b],mid+1,r);
}
} T1;
struct segment_tree {
int id[MAXN*4];
vector<dll> e[MAXN*4];
void build(int node,int l,int r) {
if(l==r) {
id[l]=node;
return ;
}
int mid=(l+r)/2;
build((node<<1),l,mid);
build((node<<1|1),mid+1,r);
}
void update(int node,int l,int r,int x,int y,int a,int b,int c,int d,int i) {
if(l>y||r<x) return ;
if(l>=x&&r<=y) {
e[node].push_back((dll){a,b,c,d,i});
return ;
}
int mid=(l+r)/2;
update((node<<1),l,mid,x,y,a,b,c,d,i);
update((node<<1|1),mid+1,r,x,y,a,b,c,d,i);
}
}T2;
struct sbtree {
int x[MAXN*4],lb[MAXN*4],y[MAXN*4];
bool ta[MAXN*4];
void psup(int node) {
if(x[(node<<1)]==-1) {
x[node]=x[(node<<1|1)];
y[node]=y[(node<<1|1)];
return ;
}
if(x[(node<<1|1)]==-1) {
x[node]=x[(node<<1)];
y[node]=y[(node<<1)];
return ;
}
if(T1.cmp(x[(node<<1|1)],x[(node<<1)],0,N)) {
x[node]=x[(node<<1)];
y[node]=y[(node<<1)];
}
else {
x[node]=x[(node<<1|1)];
y[node]=y[(node<<1|1)];
}
if(ta[(node<<1)]&&ta[(node<<1|1)]) ta[node]=1;
}
void psdn(int node) {
if(lb[node]!=-1) {
int lc=(node<<1),rc=(node<<1|1);
if(!ta[lc]) {
if(x[lc]==-1||T1.cmp(x[lc],lb[node],0,N)) x[lc]=lb[node];
if(lb[lc]==-1||T1.cmp(lb[lc],lb[node],0,N)) lb[lc]=lb[node];
}
if(!ta[rc]) {
if(x[rc]==-1||T1.cmp(x[rc],lb[node],0,N)) x[rc]=lb[node];
if(lb[rc]==-1||T1.cmp(lb[rc],lb[node],0,N)) lb[rc]=lb[node];
}
lb[node]=-1;
}
}
void build(int node,int l,int r) {
lb[node]=-1;
if(l==r) {
x[node]=-1;
y[node]=l;
return ;
}
int mid=(l+r)/2;
build((node<<1),l,mid);
build((node<<1|1),mid+1,r);
psup(node);
}
void dele(int node,int l,int r,int X) {
if(l>X||r<X) return ;
if(l==r) {
ta[node]=1;
x[node]=-1; y[node]=-1;
return ;
}
int mid=(l+r)/2;
psdn(node);
dele((node<<1),l,mid,X);
dele((node<<1|1),mid+1,r,X);
psup(node);
}
void update(int node,int l,int r,int X,int Y,int Z) {
if(l>Y||r<X) return ;
if(l>=X&&r<=Y) {
if(ta[node]) return ;
if(x[node]==-1||T1.cmp(x[node],Z,0,N)) {
x[node]=Z;
lb[node]=Z;
}
else if(lb[node]==-1||T1.cmp(lb[node],Z,0,N)) {
lb[node]=Z;
}
return ;
}
int mid=(l+r)/2;
update((node<<1),l,mid,X,Y,Z);
update((node<<1|1),mid+1,r,X,Y,Z);
psup(node);
}
}T3;
bool vis[MAXN];
void init() {
pw[0]=1;
for(int i=1;i<=N;++i) pw[i]=pw[i-1]*2%MODD;
scanf("%d%d",&n,&m);
T2.build(1,1,n);
for(int i=1;i<=m;++i) {
int l1,r1,l2,r2,a,b;
scanf("%d%d%d%d%d%d",&l1,&r1,&l2,&r2,&a,&b);
T2.update(1,1,n,l1,r1,l2,r2,a,b,i);
}
}
void dij() {
S=1;
T3.build(1,1,n);
T1.build(rt[0],0,N); rt[S]=rt[0];
T3.update(1,1,n,S,S,rt[S]);
while(1) {
if(T3.y[1]==-1||T3.x[1]==-1) break;
int u=T3.y[1]; rt[u]=T3.x[1];
T3.dele(1,1,n,u);
for(int i=T2.id[u];i;i>>=1) {
for(auto t:T2.e[i]) {
if(vis[t.i]) continue;
vis[t.i]=1;
T1.mdf(rt[n+1],rt[u],t.a,t.b);
res=0;
T3.update(1,1,n,t.l2,t.r2,rt[n+1]);
}
T2.e[i].clear();
}
}
for(int i=2;i<=n;++i) {
if(rt[i]) printf("%lld ",T1.hs[rt[i]]);
else printf("-1 ");
}
}
int main () {
freopen("sb.in","r",stdin);
freopen("1.out","w",stdout);
init();
dij();
return 0;
}
/*
5 3
1 3 1 3 1 0
4 5 1 2 4 1
2 4 1 5 5 1
*/

浙公网安备 33010602011771号