JOISC2020题解
A. Building 4
题目大意
给
个数对 ,构造一个非降序列 满足 ,且 的位置恰好有 个。 数据范围:
。
思路分析
考虑 dp,最朴素的设计就是令
打表可以发现:每个
输出方案直接倒序还原即可。
时间复杂度
代码呈现
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+5,inf=1e9;
struct Info {
int l,r;
Info(int x=inf,int y=-inf): l(x),r(y) {}
inline friend Info operator +(const Info &u,const Info &v) { return Info(min(u.l,v.l),max(u.r,v.r)); }
inline bool in(int x) { return l<=x&&x<=r; }
} dp[MAXN][2];
int n,a[MAXN],b[MAXN];
char o[MAXN];
signed main() {
scanf("%d",&n),n<<=1;
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=n;++i) scanf("%d",&b[i]);
dp[1][0]={0,0},dp[1][1]={1,1};
for(int i=2;i<=n;++i) {
if(a[i-1]<=a[i]) dp[i][0]=dp[i][0]+dp[i-1][0];
if(a[i-1]<=b[i]) dp[i][1]=dp[i][1]+dp[i-1][0];
if(b[i-1]<=b[i]) dp[i][1]=dp[i][1]+dp[i-1][1];
if(b[i-1]<=a[i]) dp[i][0]=dp[i][0]+dp[i-1][1];
++dp[i][1].l,++dp[i][1].r;
}
if(dp[n][0].in(n/2)||dp[n][1].in(n/2)) {
for(int i=n,k=0,s;i;--i) {
if((i==n||a[i]<=(s?b[i+1]:a[i+1]))&&dp[i][0].in(n/2-k)) s=0;
else s=1,++k;
o[i]="AB"[s];
}
for(int i=1;i<=n;++i) printf("%c",o[i]);
puts("");
} else puts("-1");
return 0;
}
B. Hamburg Steak
题目大意
给
个矩形,在平面上选定 个点使每个矩形内至少有一个被选定的点。 数据范围:
, ,保证有解。
思路分析
先考虑
先求出所有矩形的交,如果非空,那么任取一个都可以。
如果一边非空(最大左端点
如果两边都为空(最大左端点
接下来我们只要处理四个边界上分别恰有一个点的情况,能够证明四个点一定在四条边界构成的矩形上。
对坐标离散化,然后建立 2-SAT 模型,如
考虑分类讨论每个矩形和几条边界有交:
- 若有
或 条边界有交,显然至少有一条边界被完全包含,则这个矩形一定满足。 - 若和
条边界有交,显然直接更新对应的可能被选区间。 - 若不和区间有交,至少要一个点放在边界矩形内部,那么这种情况一定在刚刚选拐点的过程中处理过。
- 若和
条边界有交,那么限制形如 ,其中 是边界, 表示对应边界上的一个子区间,但限制关系,考虑前缀和优化,注意到 ,因此原限制条件可以拆成 组前缀变量上的限制关系,这样限制关系数就可以接受。
时间复杂度:
代码呈现
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+5,inf=1e9;
struct Rect { int lox,hix,loy,hiy; };
inline Rect merge(Rect a,Rect b) {
return {max(a.lox,b.lox),min(a.hix,b.hix),max(a.loy,b.loy),min(a.hiy,b.hiy)};
}
typedef array<int,2> Pair;
inline vector<Pair> dfs(const vector<Rect> &now,int k) {
if(!k) return vector<Pair>(0);
Rect inter{1,inf,1,inf};
for(auto re:now) inter=merge(inter,re);
int L=inter.lox,R=inter.hix,D=inter.loy,U=inter.hiy;
if(L<=R&&D<=U) return vector<Pair>(k,{L,D});
if(L<=R) {
vector <Pair> sec,ans;
for(auto re:now) sec.push_back({re.loy,re.hiy});
sort(sec.begin(),sec.end(),[&](auto I,auto J){ return I[1]<J[1]; });
int lst=0;
for(auto s:sec) if(lst<s[0]) ans.push_back({L,lst=s[1]});
if((int)ans.size()>k) ans.clear();
else while((int)ans.size()<k) ans.push_back({inf,inf});
return ans;
}
if(D<=U) {
vector <Pair> sec,ans;
for(auto re:now) sec.push_back({re.lox,re.hix});
sort(sec.begin(),sec.end(),[&](auto I,auto J){ return I[1]<J[1]; });
int lst=0;
for(auto s:sec) if(lst<s[0]) ans.push_back({lst=s[1],U});
if((int)ans.size()>k) ans.clear();
else while((int)ans.size()<k) ans.push_back({inf,inf});
return ans;
}
for(int o:{0,1,2,3}) {
int x=(o&1)?L:R,y=(o&2)?U:D;
vector <Rect> nxt;
for(auto Re:now) if(x<Re.lox||x>Re.hix||y<Re.loy||y>Re.hiy) nxt.push_back(Re);
vector <Pair> ans=dfs(nxt,k-1);
if(ans.empty()) continue;
ans.push_back({x,y});
return ans;
}
return vector<Pair>(0);
}
int U[MAXN*2][2],D[MAXN*2][2],L[MAXN*2][2],R[MAXN*2][2];
int vx[MAXN*2],vy[MAXN*2];
vector <int> G[MAXN*16];
inline void link(int u,int v) { G[u].push_back(v); }
int dfn[MAXN*16],low[MAXN*16],dcnt;
int stk[MAXN*16],tp;
bool ins[MAXN*16];
int bel[MAXN*16],scnt;
inline void tarjan(int u) {
dfn[u]=low[u]=++dcnt;
ins[stk[++tp]=u]=true;
for(int v:G[u]) {
if(!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
else if(ins[v]) low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]) {
int k; ++scnt;
do {
ins[k=stk[tp--]]=false;
bel[k]=scnt;
} while(k^u);
}
}
signed main() {
int n,k;
scanf("%d%d",&n,&k);
vector <Rect> a(n);
for(int i=0;i<n;++i) scanf("%d%d%d%d",&a[i].lox,&a[i].loy,&a[i].hix,&a[i].hiy);
auto ans=dfs(a,k);
if(!ans.empty()) {
for(auto I:ans) printf("%d %d\n",I[0],I[1]);
return 0;
}
for(auto re:a) {
vx[++vx[0]]=re.lox,vx[++vx[0]]=re.hix;
vy[++vy[0]]=re.loy,vy[++vy[0]]=re.hiy;
}
sort(vx+1,vx+vx[0]+1),vx[0]=unique(vx+1,vx+vx[0]+1)-vx-1;
sort(vy+1,vy+vy[0]+1),vy[0]=unique(vy+1,vy+vy[0]+1)-vy-1;
for(auto &re:a) {
re.lox=lower_bound(vx+1,vx+vx[0]+1,re.lox)-vx;
re.hix=lower_bound(vx+1,vx+vx[0]+1,re.hix)-vx;
re.loy=lower_bound(vy+1,vy+vy[0]+1,re.loy)-vy;
re.hiy=lower_bound(vy+1,vy+vy[0]+1,re.hiy)-vy;
}
Rect inter{1,inf,1,inf};
for(auto re:a) inter=merge(inter,re);
int l=inter.hix,r=inter.lox,d=inter.hiy,u=inter.loy;
int vcnt=0;
for(int i=l;i<=r;++i) U[i][0]=++vcnt,D[i][0]=++vcnt;
for(int i=d;i<=u;++i) L[i][0]=++vcnt,R[i][0]=++vcnt;
for(int i=l;i<=r;++i) U[i][1]=++vcnt,D[i][1]=++vcnt;
for(int i=d;i<=u;++i) L[i][1]=++vcnt,R[i][1]=++vcnt;
for(int i=l;i<r;++i) {
link(U[i][1],U[i+1][1]);
link(U[i+1][0],U[i][0]);
link(D[i][1],D[i+1][1]);
link(D[i+1][0],D[i][0]);
}
link(D[r][0],D[r][1]);
link(U[r][0],U[r][1]);
for(int i=d;i<u;++i) {
link(L[i][1],L[i+1][1]);
link(L[i+1][0],L[i][0]);
link(R[i][1],R[i+1][1]);
link(R[i+1][0],R[i][0]);
}
link(L[u][0],L[u][1]);
link(R[u][0],R[u][1]);
for(auto re:a) {
int tl,tr,tu,td;
bool il=(tl=re.lox)<=l,ir=(tr=re.hix)>=r;
bool id=(td=re.loy)<=d,iu=(tu=re.hiy)>=u;
int sum=il+ir+id+iu;
if(sum>=3) continue;
if(sum==1) {
if(il) {
link(L[td-1][1],L[td-1][0]);
link(L[tu][0],L[tu][1]);
}
if(ir) {
link(R[td-1][1],R[td-1][0]);
link(R[tu][0],R[tu][1]);
}
if(id) {
link(D[tl-1][1],D[tl-1][0]);
link(D[tr][0],D[tr][1]);
}
if(iu) {
link(U[tl-1][1],U[tl-1][0]);
link(U[tr][0],U[tr][1]);
}
}
if(sum==2) {
if(il&&iu) {
link(U[tr][0],L[td-1][0]);
link(L[td-1][1],U[tr][1]);
}
if(il&&id) {
link(L[tu][0],D[tr][1]);
link(D[tr][0],L[tu][1]);
}
if(ir&&iu) {
link(U[tl-1][1],R[td-1][0]);
link(R[td-1][1],U[tl-1][0]);
}
if(ir&&id) {
link(R[tu][0],D[tl-1][0]);
link(D[tl-1][1],R[tu][1]);
}
if(il&&ir) {
link(L[td-1][1],R[td-1][0]);
link(L[td-1][1],R[tu][1]);
link(L[tu][0],R[td-1][0]);
link(L[tu][0],R[tu][1]);
link(R[td-1][1],L[td-1][0]);
link(R[td-1][1],L[tu][1]);
link(R[tu][0],L[td-1][0]);
link(R[tu][0],L[tu][1]);
}
if(id&&iu) {
link(D[tl-1][1],U[tl-1][0]);
link(D[tl-1][1],U[tr][1]);
link(D[tr][0],U[tl-1][0]);
link(D[tr][0],U[tr][1]);
link(U[tl-1][1],D[tl-1][0]);
link(U[tl-1][1],D[tr][1]);
link(U[tr][0],D[tl-1][0]);
link(U[tr][0],D[tr][1]);
}
}
}
for(int i=1;i<=vcnt;++i) if(!dfn[i]) tarjan(i);
for(int i=d;i<=u;++i) {
bool lst0=(i==d||bel[L[i-1][0]]<bel[L[i-1][1]]);
bool now1=bel[L[i][0]]>bel[L[i][1]];
if(lst0&&now1) printf("%d %d\n",vx[l],vy[i]);
}
for(int i=d;i<=u;++i) {
bool lst0=(i==d||bel[R[i-1][0]]<bel[R[i-1][1]]);
bool now1=bel[R[i][0]]>bel[R[i][1]];
if(lst0&&now1) printf("%d %d\n",vx[r],vy[i]);
}
for(int i=l;i<=r;++i) {
bool lst0=(i==l||bel[D[i-1][0]]<bel[D[i-1][1]]);
bool now1=bel[D[i][0]]>bel[D[i][1]];
if(lst0&&now1) printf("%d %d\n",vx[i],vy[d]);
}
for(int i=l;i<=r;++i) {
bool lst0=(i==l||bel[U[i-1][0]]<bel[U[i-1][1]]);
bool now1=bel[U[i][0]]>bel[U[i][1]];
if(lst0&&now1) printf("%d %d\n",vx[i],vy[u]);
}
return 0;
}
C. Sweeping
题目大意
在平面直角坐标系上有一个等腰直角三角形坐标,其中两条直角边分别平行于 X 轴和 Y 轴,斜边连接
和 。 区域中有
个点,要进行如下 次操作:
- 插入一个点。
- 查询一个点的坐标。
- 给定
,把 的点的横坐标与 取 。 - 给定
,把 的点的纵坐标与 取 。 数据范围:
。
思路分析
考虑分治,每次处理一个矩形
对于插入操作和询问操作:判断对应点是否在矩形内部即可,不在就传到上侧或右侧三角形里递归,接下来的操作只考虑矩形内部的点。
对于一个向右推平的操作:
-
如果
,那么该操作就会把若干 的点推到右侧,用堆维护对应的点集,然后把横坐标设成 ,并把所有点连同该操作传进右侧三角形。 -
如果
,那么该操作就会把若干 的点的横坐标设成 ,可以用堆维护,但为了保证复杂度,我们对于这些横坐标相同的点用并查集合并起来,只留一个代表元插入堆中,这样就可以保证每次操作至多加入一个点入堆,且每个点只会被删除一次,单层中的操作次数为 级别。但此时对于一个向上推平出界到上侧三角形的操作,每次从堆中取出来的点实际上是一个代表元,我们要把这个代表元对应的所有点都从矩形中删除,因此我们要维护每个代表元实际的集合,由于每个点只会被删除一次,因此我们维护并查集合并关系对应的树结构,然后取出的时候在树上 BFS 一遍即可。
特别地,
时会对上侧矩形产生影响,需要传进上侧三角形。
向上推的操作也是同理,类似维护堆和并查集即可。
注意到任何一个操作始终只会单侧递归到上方三角形或右侧三角形,因此每层操作数总和为
时间复杂度
代码呈现
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1.5e6+5;
struct info { int op,x,y,id; };
struct cmpx { inline bool operator ()(const info &u,const info &v) { return u.x>v.x; } };
struct cmpy { inline bool operator ()(const info &u,const info &v) { return u.y>v.y; } };
struct DSU {
int dsu[MAXN];
inline int find(int x) { return dsu[x]==x?x:(dsu[x]=find(dsu[x])); }
inline void merge(int x,int y) { dsu[find(x)]=find(y); }
} dx,dy;
int n,px[MAXN],py[MAXN],ax[MAXN],ay[MAXN],out[MAXN],que[MAXN],hd,tl,vid[MAXN];
vector <int> gx[MAXN],gy[MAXN];
inline void solve(int l,int r,vector<info>&Q) {
if(l>r) return ;
if(l==r) {
for(auto &q:Q) if(q.op==1) ax[q.id]=l,ay[q.id]=n-l;
return ;
}
priority_queue <info,vector<info>,cmpx> qx;
priority_queue <info,vector<info>,cmpy> qy;
int k=(l+r)>>1;
vector <info> nxt[2]; //0:up,1:right
for(auto &q:Q) {
if(q.op==1) {
if(~out[q.x]) nxt[out[q.x]].push_back(q);
else ax[q.id]=px[dx.find(q.x)],ay[q.id]=py[dy.find(q.x)];
}
if(q.op==2) {
if(q.x<n-k) {
while(qy.size()&&qy.top().y<=q.x) {
int w=qy.top().id; qy.pop();
hd=1,tl=1,que[1]=w;
while(hd<=tl) {
int u=que[hd++];
if(out[u]==-1) nxt[out[u]=1].push_back({4,k+1,py[w],u});
for(int v:gy[u]) que[++tl]=v;
vector<int>().swap(gy[u]);
}
}
nxt[1].push_back(q);
} else {
hd=1,tl=0;
while(qx.size()&&qx.top().x<=n-q.x) que[++tl]=qx.top().id,qx.pop();
if(hd<=tl) {
int p=que[tl];
qx.push({4,px[p]=n-q.x,py[p],p});
for(int i=hd;i<tl;++i) gx[p].push_back(que[i]),dx.merge(que[i],p);
}
if(q.x>n-k) nxt[0].push_back(q);
}
}
if(q.op==3) {
if(q.x<k) {
while(qx.size()&&qx.top().x<=q.x) {
int w=qx.top().id; qx.pop();
hd=1,tl=1,que[1]=w;
while(hd<=tl) {
int u=que[hd++];
if(out[u]==-1) nxt[out[u]=0].push_back({4,px[w],n-k+1,u});
for(int v:gx[u]) que[++tl]=v;
vector<int>().swap(gx[u]);
}
}
nxt[0].push_back(q);
} else {
hd=1,tl=0;
while(qy.size()&&qy.top().y<=n-q.x) que[++tl]=qy.top().id,qy.pop();
if(hd<=tl) {
int p=que[tl];
qy.push({4,px[p],py[p]=n-q.x,p});
for(int i=hd;i<tl;++i) gy[p].push_back(que[i]),dy.merge(que[i],p);
}
if(q.x>k) nxt[1].push_back(q);
}
}
if(q.op==4) {
int i=q.id;
if(q.y>n-k) { nxt[out[i]=0].push_back(q); continue; }
if(q.x>k) { nxt[out[i]=1].push_back(q); continue; }
out[i]=-1,px[i]=q.x,py[i]=q.y,dx.dsu[i]=i,dy.dsu[i]=i;
gx[i].clear(),gy[i].clear(),qx.push(q),qy.push(q);
}
}
vector<info>().swap(Q);
solve(l,k-1,nxt[0]),solve(k+1,r,nxt[1]);
}
signed main() {
int m,q,vc;
ios::sync_with_stdio(false);
cin>>n>>m>>q,vc=m;
vector <info> Q; vector <int> qry;
for(int i=1,x,y;i<=m;++i) cin>>x>>y,Q.push_back({4,x,y,i}),vid[i]=i;
for(int i=m+1,op,x,y;i<=m+q;++i) {
cin>>op>>x;
if(op==4) cin>>y,Q.push_back({op,x,y,i}),vid[++vc]=i;
else if(op==1) Q.push_back({op,vid[x],0,i}),qry.push_back(i);
else Q.push_back({op,x,0,i});
}
solve(0,n,Q);
for(int i:qry) cout<<ax[i]<<" "<<ay[i]<<"\n";
return 0;
}
D. Chameleon's Love
题目大意
有
个点, 个黑点 个白点,每个点有一个初始颜色 。 保证黑点的颜色和白点的颜色分别构成一个
的排列。 每个点有一个指向的点
,满足 且 不同为黑点或白点。 定义
表示: 时 ,否则 。 每次你可以询问
,交互器会返回 中有多少个元素。 请在
次求出每个初始颜色相同的点对。 数据范围:
。
思路分析
考虑询问
如果询问
如果度数为
因此我们能求出每个
问题转成了如何求出一个点的邻域。
假如我们知道每个点是黑色还是白色,那么我们可以在其对面的一侧二分,每次考虑取出
而观察该二分,我们依赖的性质仅仅是同侧点内没有连边关系互相影响,因此我们只要任意取出一个独立集都可以求解邻域。
显然我们建出来的图一定是二分图,将其黑白染色,对每种颜色的集合都二分求一次邻域即可。
询问次数
时间复杂度
代码呈现
#include<bits/stdc++.h>
#include"chameleon.h"
using namespace std;
const int MAXN=1005;
int col[MAXN],in[MAXN],out[MAXN];
bool vis[MAXN];
vector <int> G[MAXN],ver[2];
void dfs(int u,int c) {
vis[u]=true,ver[col[u]=c].push_back(u);
for(int v:G[u]) if(!vis[v]) dfs(v,c^1);
}
void Solve(int N) {
for(int i=1;i<=2*N;++i) {
fill(vis,vis+i,0),ver[0].clear(),ver[1].clear();
for(int j=1;j<i;++j) if(!vis[j]) dfs(j,0);
for(int c:{0,1}) {
int x=0; auto it=ver[c].begin();
vector <int> Q;
for(int T:{0,1,2}) {
int l=x,r=ver[c].size()-1,p=r+1;
if(l>r) break;
Q=vector<int>(it+l,it+r+1),Q.push_back(i);
if(Query(Q)==(int)Q.size()) break;
while(l<=r) {
int mid=(l+r)>>1;
Q=vector<int>(it+l,it+mid+1),Q.push_back(i);
if(Query(Q)<(int)Q.size()) p=mid,r=mid-1;
else l=mid+1;
}
G[i].push_back(ver[c][p]),G[ver[c][p]].push_back(i),x=p+1;
}
}
}
for(int u=1;u<=2*N;++u) if(G[u].size()==3) {
int x=G[u][0],y=G[u][1],z=G[u][2];
if(Query({u,x,y})==1) out[u]=z,in[z]=u;
else if(Query({u,x,z})==1) out[u]=y,in[y]=u;
else out[u]=x,in[x]=u;
}
memset(vis,false,sizeof(vis));
for(int u=1;u<=2*N;++u) if(!vis[u]) {
int v;
if(G[u].size()==3) v=G[u][0]^G[u][1]^G[u][2]^in[u]^out[u];
else v=G[u][0];
Answer(u,v),vis[u]=vis[v]=true;
}
}
E. Making Friends on Joitter is Fun
题目大意
给
个点, 次操作加入一条有向边,然后按如下规则不断更新整个图:若 则连接 。 数据范围:
。
思路分析
容易发现双向连边关系具有传递性,因此每个点连向的一定是一个完整的双向边构成的连通块。
因此我们只要维护所有双向连边关系构成的连通块即可,对每个连通块维护入点和出点的集合,每次相当于合并两个集合。
使用启发式合并
时间复杂度
代码呈现
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=1e5+5;
unordered_set <int> in[MAXN],out[MAXN];
//in:all node link u, out: every dsu linked
int n,m,siz[MAXN],dsu[MAXN];
ll ans=0;
inline int find(int x) { return dsu[x]^x?(dsu[x]=find(dsu[x])):x; }
inline void link(int u,int v) {
u=find(u),v=find(v); if(u==v) return ;
if(in[u].size()+out[u].size()<in[v].size()+out[v].size()) swap(u,v);
ans-=1ll*siz[u]*in[u].size()+1ll*siz[v]*in[v].size();
vector <int> nxt;
for(int x:out[v]) if(out[x].count(u)) nxt.push_back(x);
for(int x:in[v]) {
x=find(x);
if(out[u].count(x)) nxt.push_back(x);
out[x].erase(v),out[x].insert(u);
}
dsu[v]=u,siz[u]+=siz[v];
for(int x:in[v]) in[u].insert(x);
for(int x:out[v]) out[u].insert(x);
ans+=1ll*siz[u]*in[u].size();
for(int x:nxt) link(u,x);
}
signed main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) siz[i]=1,dsu[i]=i,in[i].insert(i);
for(int i=1,u,v;i<=m;++i) {
scanf("%d%d",&u,&v);
int x=find(u),y=find(v);
if(x!=y) {
if(!out[y].count(x)) {
if(!in[y].count(u)) ans+=siz[y],in[y].insert(u),out[x].insert(y);
} else link(u,v);
}
printf("%lld\n",ans);
}
return 0;
}
F. Ruins 3
题目大意
给定一个长度为
,其中 每个数出现 次。 接下来会进行
次操作,每次操作同时考虑每个 :
- 如果
中有与 相同的元素,就令 。 已知最后哪
个位置非 ,求原本的序列有多少种可能。 数据范围:
。
思路分析
不妨钦定两个相同的高度属于不同的元素,最后再把答案除以
假如我们知道原序列
因此我们在 dp 的时候考虑只记录后缀的
设
若
若
否则说明
先在
根据定义,
考虑枚举
预处理组合数暴力计算即可。
时间复杂度
代码呈现
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=605,MOD=1e9+7,i2=(MOD+1)/2;
int n,p[MAXN],a[MAXN*2];
ll dp[MAXN*2][MAXN],f[MAXN],C[MAXN*2][MAXN*2];
signed main() {
scanf("%d",&n);
for(int i=0;i<=2*n;++i) for(int j=C[i][0]=1;j<=i;++j) {
C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
}
for(int i=1;i<=n;++i) scanf("%d",&p[i]),a[p[i]]=1;
f[0]=1;
for(int i=1;i<=n;++i) {
for(int j=1;j<=i;++j) {
f[i]=(f[i]+(i-j+2)*C[i-1][j-1]%MOD*f[j-1]%MOD*f[i-j]%MOD)%MOD;
}
}
dp[2*n+1][0]=1;
for(int i=2*n,s0=0,s1=0;i>=1;--i) {
if(a[i]) {
for(int j=0;j<=s1+1;++j) {
dp[i][j]=dp[i+1][j];
for(int k=1;k<=j;++k) {
dp[i][j]=(dp[i][j]+dp[i+1][j-k]*C[s1-j+k][k-1]%MOD*f[k-1]%MOD*(k+1)%MOD)%MOD;
}
}
} else {
for(int j=0;j<=s1;++j) if(j>s0) {
dp[i][j]=dp[i+1][j]*(j-s0)%MOD;
}
}
s1+=a[i],s0+=1^a[i];
}
for(int i=1;i<=n;++i) dp[1][n]=dp[1][n]*i2%MOD;
printf("%lld\n",dp[1][n]);
return 0;
}
G. Constellation 3
题目大意
给定一个
的矩形棋盘,第 列的 从棋盘上被删掉。 在现有的棋盘上有
个点,每个星星有权值,你可以保留若干个点使得棋盘上不存在任何一个完整的矩形同时包含至少两个点,最小化删除点的权值和。 数据范围:
。
思路分析
考虑建立大根笛卡尔树,对于笛卡尔树上的一个点
因此有一个简单 dp,
那么转移的时候从左右儿子转移,设
那么对于
对于
考虑整体 dp,用线段树维护,支持区间加,单点赋值,查询区间最大值,线段树合并。
时间复杂度
代码呈现
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=2e5+5;
struct SegmentTree {
static const int MAXS=8e6+5;
int ls[MAXS],rs[MAXS],tot;
ll mx[MAXS],tag[MAXS];
inline void psu(int p) { mx[p]=max(mx[ls[p]],mx[rs[p]]); }
inline void adt(int p,ll v) { if(p) mx[p]+=v,tag[p]+=v; }
inline void psd(int p) { adt(ls[p],tag[p]),adt(rs[p],tag[p]),tag[p]=0; }
inline void ins(int u,ll v,int l,int r,int &p) {
if(!p) p=++tot;
if(l==r) return mx[p]=max(mx[p],v),void();
int mid=(l+r)>>1; psd(p);
if(u<=mid) ins(u,v,l,mid,ls[p]);
else ins(u,v,mid+1,r,rs[p]);
psu(p);
}
inline void add(int ul,int ur,ll v,int l,int r,int p) {
if(ul<=l&&r<=ur) return adt(p,v);
int mid=(l+r)>>1; psd(p);
if(ul<=mid) add(ul,ur,v,l,mid,ls[p]);
if(mid<ur) add(ul,ur,v,mid+1,r,rs[p]);
psu(p);
}
inline ll qry(int ul,int ur,int l,int r,int p) {
if(ul<=l&&r<=ur) return mx[p];
int mid=(l+r)>>1; psd(p);
if(ur<=mid) return qry(ul,ur,l,mid,ls[p]);
if(mid<ul) return qry(ul,ur,mid+1,r,rs[p]);
return max(qry(ul,ur,l,mid,ls[p]),qry(ul,ur,mid+1,r,rs[p]));
}
inline void merge(int l,int r,int q,int &p) {
if(!q||!p) return p|=q,void();
if(l==r) return mx[p]=max(mx[p],mx[q]),void();
int mid=(l+r)>>1; psd(p),psd(q);
merge(l,mid,ls[q],ls[p]),merge(mid+1,r,rs[q],rs[p]);
psu(p);
}
} T;
int n,m,a[MAXN],st[MAXN][20],rt[MAXN];
inline int cmp(int x,int y) { return a[x]>a[y]?x:y; }
struct Node { int x,y,w; };
vector <Node> pi[MAXN];
inline int bit(int x) { return 1<<x; }
inline int qry(int l,int r) {
int k=__lg(r-l+1);
return cmp(st[l][k],st[r-bit(k)+1][k]);
}
inline int solve(int l,int r) {
int p=qry(l,r);
for(auto q:pi[p]) T.ins(q.y,q.w,1,n,rt[p]);
auto merge=[&](int u) {
ll x=T.qry(1,a[p],1,n,rt[p]),y=T.qry(1,a[p],1,n,rt[u]);
if(a[p]<n) T.add(a[p]+1,n,y,1,n,rt[p]),T.add(a[p]+1,n,x,1,n,rt[u]);
T.merge(1,n,rt[u],rt[p]),T.ins(a[p],x+y,1,n,rt[p]);
};
if(l<p) merge(solve(l,p-1));
if(p<r) merge(solve(p+1,r));
return p;
}
signed main() {
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]),st[i][0]=i;
for(int k=1;k<=20;++k) for(int i=1;i+bit(k-1)<=n;++i) {
st[i][k]=cmp(st[i][k-1],st[i+bit(k-1)][k-1]);
}
scanf("%d",&m);
ll sum=0;
for(int i=1,x,y,w;i<=m;++i) scanf("%d%d%d",&x,&y,&w),pi[x].push_back({x,y,w}),sum+=w;
printf("%lld\n",sum-T.mx[rt[solve(1,n)]]);
return 0;
}
H. Harvest
题目大意
在一个长度为
的圆环上有 棵树和 个人,每个人都按顺时针方向以 的速度移动。 每当一个人经过一棵树时,如果此时距离这棵树上一次被采摘过去了至少
时刻,那么这个动点就会采摘一个果子。
次询问, 秒后 会采摘几个果子。 数据范围:
。
思路分析
注意到每个人速度相同,因此当一个人
我们可以求出
那么一棵树对答案的贡献就是从某个节点沿着树边开始不断向上走,边权就表示经过这条边要花费的时间,对经过的每个节点产生
先预处理出每棵树
先考虑
容易发现我们要求的就是
每次我们搜到
然后考虑
不妨设环为
那么考虑
- 若
,那么贡献为 。 - 若
,那么贡献为 ,实际上等于 。
当然这两个贡献要和
考虑通过比较两个项的余数来拆掉取整符号:设
先考虑把
我们把
然后考虑
对于每个基环树分别统计。
时间复杂度
代码呈现
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=2e5+5;
struct FenwickTree {
ll n,tr[MAXN],s;
vector <ll> id;
inline void init() {
sort(id.begin(),id.end());
id.erase(unique(id.begin(),id.end()),id.end());
n=id.size(),fill(tr+1,tr+n+1,0);
}
inline void add(ll x,ll v) {
x=upper_bound(id.begin(),id.end(),x)-id.begin();
for(;x<=n;x+=x&-x) tr[x]+=v;
}
inline ll qry(ll x) {
x=upper_bound(id.begin(),id.end(),x)-id.begin();
for(s=0;x;x&=x-1) s+=tr[x];
return s;
}
} TR;
int n,m,q,fa[MAXN];
ll L,C,a[MAXN],b[MAXN],wf[MAXN];
struct Edge { int v; ll w; };
vector <Edge> G[MAXN];
vector <ll> vals[MAXN];
struct Query { ll t; int id;};
vector <Query> qry[MAXN];
ll ans[MAXN];
bool vis[MAXN],inc[MAXN];
ll dep[MAXN];
struct info {
ll q,r; int id;
inline friend bool operator <(const info &u,const info &v) {
return u.q<v.q;
}
};
inline void solve(int rt) {
while(!vis[rt]) vis[rt]=true,rt=fa[rt];
vector <int> cyc;
while(!inc[rt]) inc[rt]=true,rt=fa[rt],cyc.push_back(rt);
reverse(cyc.begin(),cyc.end());
int k=cyc.size();
cyc.push_back(cyc[0]);
for(int i=0;i<k;++i) {
function<void(int)>dfs0=[&](int u) {
vis[u]=true;
if(!inc[u]) {
for(auto o:vals[u]) TR.id.push_back(o+dep[u]);
}
for(auto e:G[u]) if(!inc[e.v]) {
dep[e.v]=dep[u]+e.w,dfs0(e.v);
}
};
TR.id.clear(),dfs0(cyc[i]),TR.init();
function<void(int)>dfs1=[&](int u) {
vis[u]=true;
if(!inc[u]) {
for(auto o:qry[u]) ans[o.id]-=TR.qry(o.t+dep[u]);
for(auto o:vals[u]) TR.add(o+dep[u],1),vals[cyc[i]].push_back(o+dep[u]);
}
for(auto e:G[u]) if(!inc[e.v]) dfs1(e.v);
if(!inc[u]) {
for(auto o:qry[u]) ans[o.id]+=TR.qry(o.t+dep[u]);
}
};
dfs1(cyc[i]);
}
dep[0]=0;
for(int i=1;i<=k;++i) dep[i]=dep[i-1]+wf[cyc[i]];
ll len=dep[k];
function<void(void)> calc1=[&]() {
vector <info> pt,qy;
TR.id.clear();
for(int i=0;i<k;++i) {
for(auto o:vals[cyc[i]]) {
pt.push_back({(o+dep[i])/len,(o+dep[i])%len,0});
TR.id.push_back((o+dep[i])%len);
}
for(auto o:qry[cyc[i]]) {
qy.push_back({(o.t+dep[i])/len,(o.t+dep[i])%len,o.id});
}
}
TR.init();
sort(pt.begin(),pt.end());
sort(qy.begin(),qy.end());
auto it=pt.begin();
ll cntq=0,sumq=0;
for(auto o:qy) {
while(it!=pt.end()&&it->q<=o.q) {
TR.add(it->r,1),++cntq,sumq+=it->q,++it;
}
ans[o.id]+=cntq*o.q-sumq+TR.qry(o.r);
}
};
calc1();
function<void(void)> calc2=[&]() {
TR.id.clear();
for(int i=0;i<k;++i) {
for(auto o:vals[cyc[i]]) TR.id.push_back(o+dep[i]);
}
TR.init();
for(int i=0;i<k;++i) {
for(auto o:qry[cyc[i]]) ans[o.id]-=TR.qry(o.t+dep[i]);
for(auto o:vals[cyc[i]]) TR.add(o+dep[i],1);
}
};
calc2();
}
signed main() {
ios::sync_with_stdio(false);
cin>>n>>m>>L>>C;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<=m;++i) cin>>b[i];
for(int i=1;i<=n;++i) {
fa[i]=upper_bound(a+1,a+n+1,(a[i]-C%L+L)%L)-a-1;
if(!fa[i]) fa[i]=n;
wf[i]=C/L*L+(a[i]-a[fa[i]]+L)%L;
if(wf[i]<C) wf[i]+=L;
G[fa[i]].push_back({i,wf[i]});
}
for(int i=1;i<=m;++i) {
int x=upper_bound(a+1,a+n+1,b[i])-a-1;
if(!x) x=n;
vals[x].push_back((b[i]+L-a[x])%L);
}
cin>>q;
for(int i=1;i<=q;++i) {
int v; ll t;
cin>>v>>t;
qry[v].push_back({t,i});
}
for(int i=1;i<=n;++i) if(!vis[i]) solve(i);
for(int i=1;i<=q;++i) cout<<ans[i]<<"\n";
return 0;
}
I. Stray Cat
题目大意
通信题:
Anthony.cpp
:给定一张个点 条边的无向图,给每条边一个 之间的标记。 Catherine.cpp
:每次可以知道当前节点所有出边的标记(不包括来时的边),选定一个标记,随机走一条这个标记的边,或者原路返回。要求对于任意一个起点
, Catherine.cpp
总能在次移动内到达 。 数据范围:
, 或 。
思路分析
先考虑
假如原图是一棵树,那么很好解决,对于每层的边,我们依次染 Catherine.cpp
每次就会得到
假如原图不是树,那么考虑建出 BFS 树,对于相邻两层之间的边按
然后考虑
考虑朴素做法,对于每层的边交替染
容易发现起点不在链上的时候,一定能分出第一步的上下,而后面的上下也随之确定了:
- 假如只有一个标记出现了
次,那么显然这条边就是上行边。 - 假如两个标记都只出现了
次,那么和上一次移动不同的标记是上行边。
因此我们只要保证,每个点上行边和下行边颜色不同,那么我们一旦走出了一步正确的移动,那么就可以直接找到一条最短路。
因此我们只要解决第一步在链上的情况。
考虑把链上的边染成一个特殊的串,使得我们往一个方向走若干步后就能发现我们的运动方向到底是正还是反。
注意到
如果链顶是
注意特判没走够三步就到链外的情况。
时间复杂度
代码呈现
Anthony.cpp
:
#include<bits/stdc++.h>
#include "Anthony.h"
using namespace std;
namespace {
const int MAXN=2e4+5;
namespace Task0 {
int dep[MAXN];
vector <int> G[MAXN];
inline vector<int> main(int n,int m,vector<int>&u,vector<int>&v) {
for(int i=0;i<m;++i) G[u[i]].push_back(v[i]),G[v[i]].push_back(u[i]);
queue <int> Q; Q.push(0),dep[0]=1;
while(Q.size()) {
int x=Q.front(); Q.pop();
for(int y:G[x]) if(!dep[y]) dep[y]=dep[x]+1,Q.push(y);
}
vector <int> w(m);
for(int i=0;i<m;++i) w[i]=min(dep[u[i]],dep[v[i]])%3;
return w;
}
}
namespace Task1 {
vector <int> w;
struct Edge { int v,id; };
vector <Edge> G[MAXN];
const int arr[]={0,0,1,1,0,1};
inline void dfs(int u,int fa,int len,int col) {
if(fa==-1) {
for(auto e:G[u]) w[e.id]=0,dfs(e.v,u,G[u].size()==1,1);
} else if(G[u].size()==2) {
if(len==0&&col==1) len=2;
for(auto e:G[u]) if(e.v^fa) w[e.id]=arr[len%6],dfs(e.v,u,len+1,w[e.id]^1);
} else {
for(auto e:G[u]) if(e.v^fa) w[e.id]=col,dfs(e.v,u,0,col^1);
}
}
inline vector<int> main(int n,int m,vector<int>&u,vector<int>&v) {
for(int i=0;i<m;++i) G[u[i]].push_back({v[i],i}),G[v[i]].push_back({u[i],i});
w.resize(m),dfs(0,-1,0,0);
return w;
}
}
}
vector<int> Mark(int N,int M,int A,int B,vector<int> U,vector<int> V) {
if(A>=3) return Task0::main(N,M,U,V);
else return Task1::main(N,M,U,V);
}
Catherine.cpp
:
#include<bits/stdc++.h>
#include "Catherine.h"
using namespace std;
namespace {
bool task;
namespace Task0 {
inline int Move(vector<int>&g) {
int cnt=0,s=0;
for(int i:{0,1,2}) if(g[i]) ++cnt,s+=i;
assert(cnt);
if(cnt==1) return s;
else return (4-s)%3; //f(0,1)=0, f(1,2)=1, f(2,0)=2
}
}
namespace Task1 {
const vector<int> mod[6]={{0,0,1,1,0},{0,1,1,0,1},{1,1,0,1,0},
{1,0,1,0,0},{0,1,0,0,1},{1,0,0,1,1}};
int lst=-1,chk=0;
vector <int> pat;
inline int solve(vector<int>&g) {
if(lst==-1) { //first move
if(g[0]+g[1]==2) { //chain
if(g[0]==2) return pat={0,0},0;
if(g[1]==2) return pat={1,1},1;
return pat={0,1},1;
} else { //tree
chk=1;
if(!g[0]||!g[1]) return g[0]?0:1;
return (g[0]==1)?0:1;
}
}
if(!g[0]&&!g[1]) return chk=1,-1; //leaf
if(!chk) {
if(g[0]+g[1]>1) { //tree
++g[lst],chk=1;
if(g[0]==1&&lst!=0) return 0;
if(g[1]==1&&lst!=1) return 1;
return -1;
}
pat.push_back(g[0]?0:1); //chain
if(pat.size()==5) {
chk=1;
for(int x:{0,1,2,3,4,5}) if(pat==mod[x]) return -1; //reverse
return pat.back();
} else return pat.back();
} else {
if(!g[0]||!g[1]) return g[0]?0:1; //chain
if(g[0]==1&&g[1]==1) return lst^1; //3-deg
return g[0]==1?0:1; //other
}
}
inline int Move(vector<int>&g) { return lst=solve(g); }
}
}
void Init(int A,int B) { task=(A==2); }
int Move(vector<int> y) { return task?Task1::Move(y):Task0::Move(y); }
J. Capital City
题目大意
给
个点的树,每个点有一个 之间的颜色,求一个最小的颜色集合 ,使得颜色在 中的点两两连通。 数据范围:
。
思路分析
考虑从一个点
然后我们要考虑不包含
容易发现这是标准的点分治形式,每次取
时间复杂度
代码呈现
#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
const int MAXN=2e5+5;
vector <int> G[MAXN],C[MAXN];
int n,m,ans,siz[MAXN],cur[MAXN],col[MAXN],que[MAXN],ti[MAXN],fa[MAXN];
bool vis[MAXN],inq[MAXN];
inline void solve(int u) {
function<void(int,int)> dfs3=[&](int x,int fz) {
ti[x]=u,fa[x]=fz,inq[col[x]]=0;
for(int y:G[x]) if(!vis[y]&&y!=fz) dfs3(y,x);
};
dfs3(u,0);
int hd=1,tl=1,ok=1;
inq[que[1]=col[u]]=true;
while(hd<=tl) {
int x=que[hd++];
for(int v:C[x]) if(ti[v]!=u) return ;
for(int v:C[x]) for(;v!=u&&!inq[col[fa[v]]];v=fa[v]) inq[que[++tl]=col[fa[v]]]=true;
}
if(ok) ans=min(ans,tl-1);
}
inline void dfs0(int u) {
vis[u]=true,solve(u);
for(int v:G[u]) if(!vis[v]) {
function<void(int,int)> dfs1=[&](int x,int fz) {
siz[x]=1;
for(int y:G[x]) if(y!=fz&&!vis[y]) dfs1(y,x),siz[x]+=siz[y];
};
dfs1(v,u);
int cet=0,alls=siz[v];
function<void(int,int)> dfs2=[&](int x,int fz) {
cur[x]=alls-siz[x];
for(int y:G[x]) if(y!=fz&&!vis[y]) dfs2(y,x),cur[x]=max(cur[x],siz[y]);
if(!cet||cur[x]<cur[cet]) cet=x;
};
dfs2(v,u),dfs0(cet);
}
}
signed main() {
scanf("%d%d",&n,&m),ans=m;
for(int i=1,u,v;i<n;++i) scanf("%d%d",&u,&v),G[u].push_back(v),G[v].push_back(u);
for(int i=1;i<=n;++i) scanf("%d",&col[i]),C[col[i]].push_back(i);
dfs0(1);
printf("%d\n",ans);
return 0;
}
K. Legendary Dango Maker
题目大意
本题为提交答案题。
你面前有一个
行 列的网格,每格里面放着一个粉、白、绿三色之一的团子。你每次会横向、竖向或斜向选三个连续的团子并将他们按顺序串到一起。其中,按顺序指竖直方向的团子只能以上、中、下或下、中、上的顺序串,而不能以中、上、下或中、上、下的顺序串,其他顺序以此类推。这样,你就获得了一串团子。 当且仅当一串团子的颜色顺序是绿、白、粉或粉、白、绿时,我们把这串团子称为美丽串,请求出串取最多的美丽串的方法。
数据范围:
。
思路分析
预处理出所有合法串,把不能同时被选的决策之间连边,原问题变成了一般图最大独立集问题。
考虑模拟退火,每次选一个不在独立集里的点,然后把其邻域中的点从独立集里删掉,然后再贪心加入被删除的点的邻域中可能加入的点。
设每次增广后带来的收益是
可以用 bitset
维护集合减小常数。
把初温调小一点,降温系数调大一点,简单挂一会就跑出来了。
代码呈现
#include<bits/stdc++.h>
using namespace std;
const int MAXN=505,MAXM=130011;
char mp[MAXN][MAXN];
vector <int> G[MAXM],adj[MAXN][MAXN];
int n,m,k=0,targ,xi[MAXM],yi[MAXM];
char di[MAXM];
inline void chk(int x,int y,int u,int v,char c) {
function<bool(int,int)> valid=[&](int i,int j) {
return 1<=i&&i<=n&&1<=j&&j<=m;
};
if(!valid(x+u,y+v)||!valid(x-u,y-v)) return ;
if(mp[x][y]!='W'||mp[x+u][y+v]=='W'||mp[x-u][y-v]=='W') return ;
if(mp[x+u][y+v]==mp[x-u][y-v]) return ;
++k,xi[k]=x,yi[k]=y,di[k]=c;
for(int z:{-1,0,1}) {
for(int q:adj[x+z*u][y+z*v]) G[q].push_back(k),G[k].push_back(q);
adj[x+z*u][y+z*v].push_back(k);
}
}
bitset <MAXM> res,now,tmp;
int ans,cur;
inline bool valid(int x) {
if(now[x]) return false;
for(int y:G[x]) if(now[y]) return false;
return true;
}
mt19937 rnd(time(0));
signed main() {
scanf("%d%d%d",&n,&m,&targ);
for(int i=1;i<=n;++i) scanf("%s",mp[i]+1);
for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) {
chk(i,j,1,0,'|'),chk(i,j,0,1,'-'),chk(i,j,1,1,'\\'),chk(i,j,1,-1,'/');
}
for(int i=1;i<=k;++i) if(valid(i)) {
++ans,++cur,now[i]=res[i]=1;
}
for(long double T=1;ans<targ;T*=0.999999) {
int id,bck=cur; tmp=now;
do id=rnd()%k+1; while(now[id]);
++cur,now[id]=1;
for(int x:G[id]) if(now[x]) {
--cur,now[x]=0;
for(int y:G[x]) if(valid(y)) ++cur,now[y]=1;
}
if(cur>ans) ans=cur,res=now;
else if(cur>=bck||uniform_real_distribution<>(0,1)(rnd)<expl((cur-bck)/T));
else cur=bck,swap(now,tmp);
}
for(int i=1;i<=k;++i) if(res[i]) mp[xi[i]][yi[i]]=di[i];
for(int i=1;i<=n;++i) printf("%s\n",mp[i]+1);
return 0;
}
L. Treatment Project
题目大意
给一个长度为
的序列,初始全是 ,每个早上与 相邻的位置都会变成 ,有 个操作,第 个会在第 天晚上把 区间设成 ,花费为 。 求把整个序列还原成全
的最小代价。 数据范围:
。
思路分析
考虑把序列关于时间的图像画在二维平面上,那么对于一个操作,相当于把斜边为
而如果两个等腰直角三角形相交,那么就会保留他们外侧的两条斜边生成一个更大的等腰直角三角形。
因此我们考虑 dp:
容易发现这是一个最短路模型,可以直接主席树优化建图,当然也有更简单的做法:
注意到该图的权值附加在点上,在这种图上求 Dijkstra 时每个点只会加入堆中一次,因此直接按
时间复杂度
代码呈现
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=1e5+5;
const ll inf=1e18;
int n,m;
struct Node { int l,r,t,c; } a[MAXN];
ll dp[MAXN],val[MAXN<<2][2];
bool del[MAXN];
priority_queue <array<ll,2>,vector<array<ll,2>>,greater<array<ll,2>>> Q;
inline void psu(int p) {
val[p][0]=min(val[p<<1][0],val[p<<1|1][0]);
val[p][1]=min(val[p<<1][1],val[p<<1|1][1]);
}
inline void build(int l=1,int r=n,int p=1) {
if(l==r) {
val[p][0]=del[l]?inf:a[l].l-a[l].t;
val[p][1]=del[l]?inf:a[l].l+a[l].t;
return ;
}
int mid=(l+r)>>1;
build(l,mid,p<<1),build(mid+1,r,p<<1|1),psu(p);
}
inline void upd(int ul,int ur,int k,ll lim,ll w,int l=1,int r=n,int p=1) {
if(val[p][k]>lim) return ;
if(l==r) {
Q.push({dp[l]=w+a[l].c,l});
return val[p][0]=val[p][1]=inf,void();
}
int mid=(l+r)>>1;
if(ul<=mid) upd(ul,ur,k,lim,w,l,mid,p<<1);
if(mid<ur) upd(ul,ur,k,lim,w,mid+1,r,p<<1|1);
psu(p);
}
signed main() {
scanf("%d%d",&m,&n);
for(int i=1;i<=n;++i) scanf("%d%d%d%d",&a[i].t,&a[i].l,&a[i].r,&a[i].c);
sort(a+1,a+n+1,[&](auto x,auto y){ return x.t<y.t; });
fill(dp+1,dp+n+1,inf);
for(int i=1;i<=n;++i) if(a[i].l==1) Q.push({dp[i]=a[i].c,i}),del[i]=true;
build();
while(Q.size()) {
int i=Q.top()[1]; Q.pop();
upd(1,i,0,a[i].r-a[i].t+1,dp[i]);
upd(i,n,1,a[i].r+a[i].t+1,dp[i]);
}
ll ans=inf;
for(int i=1;i<=n;++i) if(a[i].r==m) ans=min(ans,dp[i]);
printf("%lld\n",ans==inf?-1ll:ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】