模拟赛

模拟赛 2025

10.24 NOIP 模拟赛 十一

100+100+50+100=350 pts,rk1.

比较抽象,又被 T1 创飞了,写了将近 3 小时。

T2 暴力拿满,HHHOJ 机子跑的太快了。

T1

给定一个自然数 \(N(N\le 10^6)\),找出一个 \(M\),使得 \(M > 0\)\(M\)\(N\) 的倍数,并且 \(M\)\(10\) 进制表示只包含 \(0\)\(1\)。求最小的 \(M\)。无解输出 \(-1\)

原题 lg P2841,不过数据加强了一点,但不用输出 \(M \div N\)

bfs,将模 \(N\) 余数相同的剪枝。

据说可以加强到 \(N\le 10^9\),但我不会。

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof x)
#define MC(x,y) memcpy(x,y,sizeof x)
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define PSL pair<string,ll>
#define lb(x) (x&-x)
using namespace std;
const int N=1e6+5,M=1e5+5;
const ll INF=1ll<<60,mod=998244353;
int n;
bool vis[N];
queue<PSL> q;
int main(){
	IOS;cin>>n;
	q.push({"1",1});
	vis[1]=1;
	while(!q.empty()){
		PSL tp=q.front();q.pop();
		if(!(tp.second*10%n)) cout<<tp.first+"0"<<"\n",exit(0);
		if(!((tp.second*10+1)%n)) cout<<tp.first+"1"<<"\n",exit(0);
		if(!vis[tp.second*10%n]){
			q.push({tp.first+"0",tp.second*10%n});
			vis[tp.second*10%n]=1;
		}
		if(!vis[(tp.second*10+1)%n]){
			q.push({tp.first+"1",(tp.second*10+1)%n});
			vis[(tp.second*10+1)%n]=1;
		}
	}
	return 0;
} 


T2

我们定义两个 \(01\) 串的相似值为:如果两个串在第 \(i\) 位是一样的,那么他们的相似值要加上 \(w_i\)
现在给你 \(m\) 个长度为 \(n\)\(01\) 串,然后有 \(q\) 个询问,每一次给定一个长度为 \(n\)\(01\) 串,和一个值 \(k\),求在这 \(m\) 个串中,有多少个串和它的相似值不超过 \(k\)
\(n \le 15,k \le 30,m\le 5\times 10^5\)

\(f_{S,k}\) 表示询问为 \(S\) 时小于等于 \(k\) 的个数,暴力枚举。复杂度 \(\mathcal{O(2^{2n})}\) 居然能跑过。

正解:

题目代价可以转化为:将第 \(i\) 位取反的代价为 \(w_i\),问能在小于等于 \(k\) 的代价将其变成与 \(S\) 完全不同的串的数量。

\(f_{i,S,k}\) 表示第 \(1 \sim i\) 位与 \(S\) 完全不同,第 \(i+1 \sim n\) 位与 \(S\) 完全相同,且代价小于等于 \(k\) 的串的数量。

有转移:

\[f_{i,S,k}= \begin{cases} f_{i-1,S,k},w_i>k \\ f_{i-1,S,k}+f_{i-1,S \setminus i},w_i\le k \end{cases} \]

初始状态 \(f_{0,S,k}=num_S\),其中 \(num_S\) 表示给定的 \(01\) 串为 \(S\) 的个数。

每次询问答案即为 \(f_{n,S,k}\)。复杂度 \(\mathcal{O(2^n n k + q)}\)

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof x)
#define MC(x,y) memcpy(x,y,sizeof x)
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define lb(x) (x&-x)
using namespace std;
const int N=5e5+5,M=15+5,P=(1<<15)+5;
const ll INF=1ll<<60,mod=998244353;
int n,m,q,w[M],a[N],b[P],f[2][P][M<<1],num[P];
int main(){
	IOS;cin>>n>>m>>q;
	for(int i=1;i<=n;i++) cin>>w[i];
	for(int i=1;i<=m;i++){
		for(int j=0;j<n;j++){
			char ch;cin>>ch;
			if(ch=='1') a[i]|=1<<j;
		}
		num[a[i]]++;
	}
	int U=(1<<n)-1;
	for(int t=0;t<=U;t++){
		for(int j=0;j<=30;j++) f[0][t][j]=num[t];
	}
	for(int i=1;i<=n;i++){
		MS(f[i&1],0);
		for(int t=0;t<=U;t++){
			for(int j=0;j<=30;j++){
				if(j>=w[i]) f[i&1][t][j]=f[(i&1)^1][t][j-w[i]];
				f[i&1][t][j]+=f[(i&1)^1][t^(1<<(i-1))][j];
			}
		}
	}
	for(int i=1,x,k;i<=q;i++){
		x=0;
		for(int j=0;j<n;j++){
			char ch;cin>>ch;
			if(ch=='1') x|=1<<j;
		}
		cin>>k;
		cout<<f[n&1][x][k]<<"\n";
	}
	return 0;
}


T3

咕咕咕。

T4

给你 \(n\) 个数,对于每个数 \(a_i\),判断是否存在一个 \(a_j\),使得 \(a_i \& a_j=0\)\(\&\) 表示按位与。

SOSDP 板子,等价于是否存在 \(a_j \subseteq U\setminus a_i)\),不知为何放 T4。

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof x)
#define MC(x,y) memcpy(x,y,sizeof x)
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define lb(x) (x&-x)
using namespace std;
const int N=(1<<20)+5,M=1e5+5;
const ll INF=1ll<<60,mod=998244353;
int n,a[N];
bool f[N];
int main(){
	IOS;cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i],f[a[i]]=1;
	int U=(1<<20)-1;
	for(int t=0;t<=U;t++){
		for(int i=0;i<20;i++){
			if(!((t>>i)&1)) continue;
			f[t]|=f[t^(1<<i)];
		}
	}
	for(int i=1;i<=n;i++) cout<<f[U^a[i]]<<" ";
	cout<<"\n";
	return 0;
}


10.25 NOIP 模拟赛 十二

90+0+0+0=90pts,rk9
被 T1 创飞,导致 T2 想出正解但没写完,糖丸了。

T1

\(n\) 个点,给定 \(a_i,b_i (2 \le i \le n)\),表示 \(i\)\([a_i,i]\) 连权值为 \(1\) 的边,\([b_i,i]\)\(i\) 连权值为 \(1\) 的边。
\(dis_{i,j}\) 表示 \(i\)\(j\) 的最短路,求所有的 \(dis_{i,j}(1\le i,j \le n)\)。答案输出 \(dis_{i,j} \times (i+j)\) 的异或和。
\(n\le 6000\)

赛时想了一个假的做法,但拿了高达 90pts。

正解:

考虑枚举出发点 \(s\) ,设 \(f_i\) 表示 \(s\)\(i\) 的最短路。有 \(f_s=0\)

对于 \(i>s\),会发现最短路肯定是 \(s\) 向右一直跳到一个 \(j\),再向左跳若干步到达 \(i\)(可以不往左挑)。其他情况不是最优的。

对于 \(i<s\),最短路是由 \(s\) 一直向左跳到 \(i\);或是先向左跳到 \(j(i < j \le s)\),再跳到 \(k(k>s)\),最后跳到 \(i\)

计算可以设 \(pos_w\) 表示所有的 \(i\) 满足 \(f_i = w\),它最小往左能跳到的位置。

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof x)
#define MC(x,y) memcpy(x,y,sizeof x)
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define lb(x) (x&-x)
#define int ll
using namespace std;
const int N=6e3+5,M=1e5+5;
const ll INF=1ll<<60,mod=998244353;
int n,a[N],b[N],pos[N],f[N],ans;
void solve(int s){
	MS(pos,0);MS(f,0x3f);
	f[s]=0;pos[0]=s;
	for(int i=s+1,x=0;i<=n;i++){//往右跳
		x++;
		for(;x && pos[x-1]>=b[i];x--);
		pos[f[i]=x+1]=i;
	}
	MS(pos,0x3f);
	for(int i=n,x=INF;i;i--){//从 i 右边某个点跳过来
		x=min(x+1,f[i]);
		for(;x && pos[x-1]<=i;x--);
		f[i]=min(f[i],x+1);
		pos[f[i]]=min(pos[f[i]],a[i]);
	}
	for(int i=1;i<=n;i++) ans^=f[i]*(i+s);
}
signed main(){
	IOS;cin>>n;
	for(int i=2;i<=n;i++) cin>>a[i];
	for(int i=2;i<=n;i++) cin>>b[i];
	for(int i=1;i<=n;i++) solve(i);
	cout<<ans<<"\n";
	return 0;
}

T2

有⼀棵 \(n\) 个节点的树,每条边⻓度为 \(1\),颜⾊为⿊或⽩。可以执⾏若⼲次如下操作:

  • 选择⼀条简单路径,反转路径上所有边的颜⾊。

对于某些边,要求在操作结束时为某⼀种颜⾊。给定每条边的初始颜⾊,求最⼩操作数,以及满⾜操作数最⼩时,最⼩的操作路径⻓度和。

可通过将目标颜色和当前颜色的转化,将问题转化为:给定若干个点的颜色,其余点的颜色可以任意取,求答案。

树形 dp,设 \(f_{i,0/1}\) 表示 \(i\) 到它父亲的这条边为 \(0/1\) 这种颜色的最优答案。

可以将与 \(i\) 这个点连的边合并成一条路径,两个单独的路径合并可以使操作次数减 \(1\)。分别考虑奇数条边与偶数条边的转移即可。

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof x)
#define MC(x,y) memcpy(x,y,sizeof x)
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define lb(x) (x&-x)
using namespace std;
const int N=1e5+5,M=1e5+5;
const ll INF=1e9,mod=998244353;
int n;
PII f[N][2];
vector<PII> to[N];
PII operator + (const PII xx,const PII yy){
	return {xx.first+yy.first,xx.second+yy.second};
}
void dfs(int u,int fa,int fc){
	PII q={0,0},p={INF,INF};//q 0,p 1
	if(fc==1) f[u][0]={INF,INF};
	if(fc==0) f[u][1]={INF,INF};
	for(PII ttp:to[u]){
		int v=ttp.first,c=ttp.second;
		if(v==fa) continue;
		dfs(v,u,c);
		PII tp=p,tq=q;
		p=min(tp+f[v][0],tq+f[v][1]);
		q=min(tp+f[v][1],tq+f[v][0]);
	}
	if(fc==2 || fc==1) f[u][1]=min(PII{q.first+1,q.second+1},PII{p.first,p.second+1});
	if(fc==2 || fc==0) f[u][0]=min(PII{p.first+1,p.second},q);
}
int main(){
	IOS;cin>>n;
	for(int i=1,u,v,c,d;i<n;i++){
		cin>>u>>v>>c>>d;
		c^=(d==1);//转换条件
		if(d==2) c=2;
		to[u].eb(v,c);
		to[v].eb(u,c);
	}
	dfs(1,0,2);
	cout<<f[1][0].first/2<<" "<<f[1][0].second<<"\n";
	return 0;
}


T3

咕咕咕。

T4

给定⻓度为 \(n\) 的正整数序列 ,以及 \(q\) 个操作。每个操作是以下 \(2\) 种之⼀:

  • \(1~~pos~~val\):将 \(a_{pos}\) 改为 \(val\)
  • \(2~~l~~r\): 询问如果从 \(a_{l,\dots,r}\) 中选择三个元素作为三⻆形三条边的⻓度,能够围出的三⻆形的最⼤周⻓是多少。请注意,每个元素最多被选⼀次。

暴力做法为将 \(a_{l,\dots,r}\) 部分从大到小排序后找到第一个 \(i\) 满足 \(a_i+a_{i+1}>=a_{i+2}\) 即为答案。

会发现不存在答案的情况的序列最长为斐波那契数列,最长只有 \(60\) 左右。因此只用保留前 \(60\) 个数即可。用线段树维护。

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof x)
#define MC(x,y) memcpy(x,y,sizeof x)
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define lb(x) (x&-x)
using namespace std;
const int N=1e5+5,M=1e5+5;
const ll INF=1ll<<60,mod=998244353;
int n,q,a[N];
struct Tree{
	int l,r,cnt[66],len;
	#define l(p) t[p].l
	#define r(p) t[p].r
	#define cnt(p,x) t[p].cnt[x]
	#define len(p) t[p].len
}t[N<<2];
int Cnt[66],Len,b[66];
void out(int p){
	for(int i=1;i<=len(p);i++) cout<<cnt(p,i)<<" ";cout<<"\n";
}
void up(int p){
	len(p)=1;
	for(int l1=1,l2=1;len(p)<=60 && (l1<=len(p<<1) || l2<=len(p<<1|1));len(p)++){
		if(cnt(p<<1,l1)>=cnt(p<<1|1,l2)) cnt(p,len(p))=cnt(p<<1,l1++);
		else cnt(p,len(p))=cnt(p<<1|1,l2++);
	}
	len(p)--;
}
void merge(int p){
	int LL=1;MS(b,0);
	for(int l1=1,l2=1;LL<=60 && (l1<=Len || l2<=len(p));LL++){
		if(Cnt[l1]>=cnt(p,l2)) b[LL]=Cnt[l1++];
		else b[LL]=cnt(p,l2++);
	}
	Len=LL-1;MC(Cnt,b);
}
void Build(int p,int l,int r){
	l(p)=l,r(p)=r;
	if(l==r) return cnt(p,len(p)=1)=a[l],void();
	int mid=(l+r)>>1;
	Build(p<<1,l,mid);Build(p<<1|1,mid+1,r);
	up(p);
}
void update(int p,int x,int d){
	if(l(p)==r(p)) return cnt(p,1)=d,void();
	int mid=(l(p)+r(p))>>1;
	if(x<=mid) update(p<<1,x,d);
	else update(p<<1|1,x,d);
	up(p);
}
void query(int p,int l,int r){
	if(l<=l(p) && r(p)<=r) return merge(p),void();
	int mid=(l(p)+r(p))>>1;
	if(l<=mid) query(p<<1,l,r);
	if(mid<r) query(p<<1|1,l,r);
}
ll solve(int l,int r){
	MS(Cnt,0);Len=0;
	query(1,l,r);
	if(Len<=2) return 0;
	for(int i=1;i<=Len-2;i++){
		if(Cnt[i+1]+Cnt[i+2]>=Cnt[i]) return 1ll*Cnt[i]+Cnt[i+1]+Cnt[i+2];
	}
	return 0;
}
int main(){
	IOS;cin>>n>>q;
	for(int i=1;i<=n;i++) cin>>a[i];
	Build(1,1,n);
	for(int i=1,op,l,r;i<=q;i++){
		cin>>op>>l>>r;
		if(op==1) update(1,l,r);
		else cout<<solve(l,r)<<"\n";
	}
	return 0;
}


10.26 NOIP 模拟赛 十三

100+0+100+0=200 pts,rk5。

神秘比赛放两道黑。T4 73pts 很好想,懒得写了。

有大神 Vector_net T4 假做法拿满。

T1

AGC062A

神秘题,打表发现只有在 \(A\dots AB\dots B\) 时输出 \(B\)

T2

lg-P9339

黑题不会。

T3

给出了一个 \(N\) 个点 \(M\) 条边的有向图。求有多少个有序点对 \((a,b)\),满足至少存在一个点 \(c\) 以及从 \(c\)\(a\) 的一条路径 \(L_a\)\(c\)\(b\) 的一条路径 \(L_b\),使得 \(L_a\) 的长度是 \(L_b\) 的两倍(长度指的经过的边的数目)\((|L_a|,|L_b|\ge 0)\)
注意不一定是简单路径。

bfs,每次 \(a\) 走两步,\(b\) 走一步,分步进行。

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof x)
#define MC(x,y) memcpy(x,y,sizeof x)
#define PLL pair<ll,ll>
#define lb(x) (x&-x)
using namespace std;
const int N=3e3+5,M=1e5+5;
const ll INF=1ll<<60,mod=998244353;
int n,m;
bool vis[N][N][5];
vector<int> to[N];
struct node{
	int u,v,stp;
};
int main(){
	IOS;cin>>n>>m;
	for(int i=1,u,v;i<=m;i++){
		cin>>u>>v;
		to[u].pb(v);
	}
	int ans=0;
	queue<node> q;
	for(int i=1;i<=n;i++){
		vis[i][i][3]=1;
		q.push({i,i,3});
	}
	while(!q.empty()){
		node tp=q.front();q.pop();
		if(tp.stp==3){
			for(int v:to[tp.u]){
				if(vis[v][tp.v][1]) continue;
				q.push({v,tp.v,1});
				vis[v][tp.v][1]=1;
			}
		}
		else if(tp.stp==1){
			for(int v:to[tp.v]){
				if(vis[tp.u][v][2]) continue;
				q.push({tp.u,v,2});
				vis[tp.u][v][2]=1;
			}
		}
		else{
			for(int v:to[tp.v]){
				if(vis[tp.u][v][3]) continue;
				q.push({tp.u,v,3});
				vis[tp.u][v][3]=1;
			}
		}
	}	
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++) ans+=vis[i][j][3];
	}
	cout<<ans<<"\n";
	return 0;
}

T4

lg-P6881

黑不会,还没订正。

10.27 NOIP 模拟赛 十四

100+5+0+0=105pts,rk14。

T2 拼尽全力想出正解但写挂了。我太菜了

T1

你在一条数轴 \(1 \sim N\) 上放点,初始时 \(0\)\(N+1\) 已有点。
规则:选择间隔最大的两点(包括边界点,多个最长时选最左的),在两点中间的位置放入新点(如果两点中间的点数恰好是奇数,则放在正中间,否则停在正中间的左边位置)。
每个第 \(i\) 次放入的点有一个 \(a_i\),要求放入后,离它最近的 非边界点 的距离 严格大于 \(a_i\),否则这个点会消失。
你可以花费 \(1\) 的代价使 \(a_i\)\(1\)
求最小的代价,使让所有 \(M\) 个点都成功放下。
\(N\le 10^{12},M\le 10^6\)

简单题,考虑求出 \(pos_i\) 表示 \(i\) 放的位置。用优先队列维护每个区间即可。最后统计答案。

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof x)
#define MC(x,y) memcpy(x,y,sizeof x)
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define lb(x) (x&-x)
using namespace std;
const int N=1e6+5,M=1e5+5;
const ll INF=1ll<<60,mod=998244353;
int m;
ll n,a[N],v[N];
struct lines{
	ll first,second;
};
priority_queue< lines,vector<lines>,greater<lines> > s;
bool operator > (const lines xx,const lines yy){
	if(xx.first==yy.first) return xx.second>yy.second;
	return xx.first<yy.first;
}
struct node{
	ll pos,id;
}b[N];
int main(){
	IOS;cin>>n>>m;
	for(int i=1;i<=m;i++) cin>>a[i],b[i].id=i;
	b[0].pos=-INF,b[m+1].pos=INF;
	s.push((lines){n,1});
	for(int i=1;i<=m;i++){
		lines tp=s.top();s.pop();
		ll len=(tp.first+1)>>1;
		b[i].pos=tp.second+len-1;
		if((tp.first-1)>>1){
			s.push((lines){(tp.first-1)>>1,tp.second});
		}
		if(tp.first>>1){
			s.push((lines){tp.first>>1,b[i].pos+1});
		}
	}
	sort(b+1,b+1+m,[&](const node xx,const node yy){
		return xx.pos<yy.pos;
	});
	for(int i=1;i<=m;i++){
		v[b[i].id]=min(b[i].pos-b[i-1].pos,b[i+1].pos-b[i].pos);
	}
	ll ans=0;
	for(int i=1;i<=m;i++){
		if(a[i]-v[i]+1>0) ans+=a[i]-v[i]+1;
	}
	cout<<ans<<"\n";
	return 0;
}

T2

地图上有 \(n\) 个点,构成一棵树形结构。初始你在 \(1\) 号点,对于 \(2 ∼ n\) 中的每个点,上面都恰有一只怪兽。
每只怪兽都可以用一个二元组 \((a_i,b_i)\) 表示。
当你第一次碰到它时,它会与你开战,先对你造成 \(a_i\) 点伤害,战斗结束后你可以恢复 \(b_i\)点血量。此后再次经过它所在节点不会产生任何影响。
你可以在地图上移动并决策打怪兽的顺序,但如果某一时刻你的血量变成负数,那么你就输了。求取得最终胜利,初始血量的最小值。

神秘贪心题。

题目转化为初始血量为 \(0\),求在所有时刻血量最小值的最大值。

考虑将两个怪 \((a_1,b_1),(a_2,b_2)\) 合并,先打 \(1\) 在打 \(2\),会有:

\[(a',b')= \begin{cases} (a_1,b_1-a_2+b_2),b_1 \ge a_2\\ (a_1-b_1+a_2,b_2),b_1 < a_2 \\ \end{cases} \]

在考虑两只怪的优先级问题。

称一只怪 \(i\) 有益当且仅当 \(a_i \le b_i\)

如果在菊花图上,会有一个打怪策略:

  • 若一些怪有益,按 \(a_i\) 从小到大排。
  • 若一些怪无益,按 \(b_i\) 从大到小排。
  • 有益在无益前面。

这个顺序称为怪的优先级。

将其放到树上,如果一个点 \(u\) 的优先级最高的儿子 \(v\)。若它优先级比 \(v\) 高,因为 \(u\) 是所有能打的怪中优先级最高的的,那么打完 \(u\) 之后肯定会先打 \(v\)。那么将 \(u\)\(v\) 合并,直到最后只剩一个点为止。

可以写并查集维护或是 dfs + 启发式合并。

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof x)
#define MC(x,y) memcpy(x,y,sizeof x)
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define lb(x) (x&-x)
using namespace std;
const int N=1e5+5,M=1e5+5;
const ll INF=1ll<<60,mod=998244353;
int n,ff[N];
ll a[N],b[N];
vector<int> to[N];
struct node{
	ll x,y;
	int id;
};
bool operator > (const node xx,const node yy){
	if(xx.x>=xx.y && yy.x<yy.y) return 1;
	else if(xx.x<xx.y && yy.x>=yy.y) return 0;
	else if(xx.x<=xx.y && yy.x<=yy.y) return xx.x>yy.x;		
	else return xx.y<yy.y;		
}
priority_queue<node,vector<node>,greater<node> > q[N];
void dfs(int u,int fa){
	ff[u]=fa;
	for(int v:to[u]){
		if(v==fa) continue;
		dfs(v,u);
		if(q[v].size()>q[u].size()) swap(q[v],q[u]);//启发式合并
	}
	for(int v:to[u]){
		if(v==fa) continue;
		while(!q[v].empty()) q[u].push(q[v].top()),q[v].pop();
	}
	while(!q[u].empty()){
		auto tp=q[u].top();
		if(node{a[u],b[u]}>tp){
			if(b[u]>=tp.x) b[u]-=tp.x-tp.y;
			else a[u]-=b[u]-tp.x,b[u]=tp.y;
			q[u].pop();
		}
		else break;
	}
	q[u].push({a[u],b[u],u});
}
int main(){
	IOS;cin>>n;
	for(int i=2;i<=n;i++) cin>>a[i]>>b[i];
	for(int i=1,u,v;i<n;i++){
		cin>>u>>v;
		to[u].pb(v);
		to[v].pb(u);
	}
	dfs(1,0);
	while(!q[1].empty()){
		node tp=q[1].top();q[1].pop();
		if(b[0]>=tp.x) b[0]-=tp.x-tp.y;
		else a[0]-=b[0]-tp.x,b[0]=tp.y;			
	}
	cout<<a[0]<<"\n";
	return 0;
}

T3

给定一棵 \(n\) 个节点的树,根为 \(1\),第 \(i\) 个点有一个权值 \(a_i\)
对每个点 \(u\),这次询问答案为其所在子树外的所有点中,选两个点 \(i,j\)\(ai,aj\) 异或后的最大值,如果选不出两个点,则认为 \(u\) 的答案是 \(0\)

原题 lg-P8511

01-Trie 维护,可以先求出整棵树的异或最大值以及位置,设为 \(x,y\),会有除了 \(1\)\(x\) 的路径以及 \(1\)\(y\) 路径上的点以外的点的答案都为 \(a_x \oplus a_y\)。再单独求 \(1\)\(x\) 的路径以及 \(1\)\(y\) 路径上的点的答案即可。

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof x)
#define MC(x,y) memcpy(x,y,sizeof x)
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define lb(x) (x&-x)
using namespace std;
const int N=5e5+5,M=N*61+5;
const ll INF=1ll<<60,mod=998244353;
int n,cnt=1,nxt[M][2],num[M],ff[N];
ll a[N];
struct Trie{
	void clear(){
		MS(nxt,0);MS(num,0);cnt=1;
	}
	void add(ll x,int id){
		int p=1;
		for(int i=59;~i;i--){
			int ch=(x>>i)&1;
			if(!nxt[p][ch]) nxt[p][ch]=++cnt;
			p=nxt[p][ch];
		}
		num[p]=id;
	}
	int query(ll x){
		int p=1;
		for(int i=59;~i;i--){
			int ch=(x>>i)&1;
			if(nxt[p][ch^1]) p=nxt[p][ch^1];
			else p=nxt[p][ch];
		}
		return num[p];
	}
}tr;
vector<int> to[N];
int ax,ay,dep[N],is[N];
ll maxx,ans[N],maxn[N];
void dfs1(int u){
	dep[u]=dep[ff[u]]+1;
	tr.add(a[u],u);
	int id=tr.query(a[u]);
	if((a[id]^a[u])>maxx){
		maxx=a[id]^a[u],ax=u,ay=id;
	}
	for(int v:to[u]) dfs1(v);
}
int Lca(int u,int v){
	if(dep[u]>dep[v]) swap(u,v);
	while(dep[u]<dep[v]) is[v]=2,v=ff[v];
	if(u==v) return u;
	while(u^v){
		is[u]=3,is[v]=2,u=ff[u],v=ff[v];
	}
	return u;
}
void dfs3(int u){
	tr.add(a[u],u);
	int id=tr.query(a[u]);
	maxn[u]=max(maxn[u],a[id]^a[u]);
	for(int v:to[u]){
		dfs3(v);
		maxn[u]=max(maxn[u],maxn[v]);
	}
}
void dfs2(int u,int fa,int ID){
	maxn[u]=ans[u]=maxn[fa];
	tr.add(a[u],u);
	maxn[u]=max(maxn[u],a[tr.query(a[u])]^a[u]);
	for(int v:to[u]){
		if(is[v]==1 || is[v]==ID) continue;
		dfs3(v);
		maxn[u]=max(maxn[u],maxn[v]);
	}
	for(int v:to[u]){
		if(is[v]==ID || is[v]==1) dfs2(v,u,ID);
	}
}
int main(){
	IOS;cin>>n;
	for(int i=2;i<=n;i++){
		cin>>ff[i];
		to[ff[i]].pb(i);
	}
	for(int i=1;i<=n;i++) cin>>a[i];
	dfs1(1);
	int lc=Lca(ax,ay),nc=lc;
	while(lc) is[lc]=1,lc=ff[lc];
	int ttt=0;
	for(int v:to[nc]) ttt+=is[v]!=0;
	for(int i=1;i<=n;i++) if(!is[i]) ans[i]=maxx;
	tr.clear();dfs2(1,0,2);
	MS(maxn,0);
	if(ttt==2) tr.clear(),dfs2(1,0,3);
	for(int i=1;i<=n;i++) cout<<ans[i]<<"\n";
	return 0;
}

T4

比较抽象。

10.28 NOIP 模拟赛 十五

100+100+0+0=200pts,rk3

被 T2 暴杀,导致 T3 想出做法来不及写。

T1

给你一棵 \(n\) 个节点的树, 每次询问包含第 \(i\) 条边的树上最长路径长度。

糖题,但我写了 1 个小时。我是废物

求一下直径即可。也可以换根 dp,但要特判在根的情况。

Code

屎山代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof x)
#define MC(x,y) memcpy(x,y,sizeof x)
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define lb(x) (x&-x)
using namespace std;
const int N=1e6+5,M=1e5+5;
const ll INF=1ll<<60,mod=998244353;
int n,d1,d2,dis[N],ans[N],pos[N],tot,bl[N],g[N],Dis[N],Id[N];
PII ff[N],e[N];
vector<PII> to[N];
bool vis[N],is[N];
void dfs1(int u,int fa){
	for(PII tp:to[u]){
		int v=tp.first,id=tp.second;
		if(v==fa) continue;
		ff[v]={u,id};
		dis[v]=dis[u]+1;
		dfs1(v,u);
	}
}
void dfs2(int u,int fa,int tf){
	for(PII tp:to[u]){
		int v=tp.first,id=tp.second;
		if(v==fa) continue;
		if(is[v]) dfs2(v,u,tf),bl[id]=tf;
		else if(is[u]){
			dis[v]=dis[u]+1;
			dfs2(v,u,u);
			Dis[u]=max(Dis[u],Dis[v]+1);bl[id]=u;
		}
		else{
			dis[v]=dis[u]+1;
			dfs2(v,u,tf);
			Dis[u]=max(Dis[u],Dis[v]+1);bl[id]=tf;
		}
	}
}
void get_d(){
	PII tp={0,1};
	dfs1(1,0);
	for(int i=1;i<=n;i++) tp=max(tp,{dis[i],i});
	d1=tp.second;
	MS(ff,0);MS(dis,0);dfs1(d1,0);
	tp={0,d1};
	for(int i=1;i<=n;i++) tp=max(tp,{dis[i],i});
	d2=tp.second;
}
int main(){
	IOS;cin>>n;
	for(int i=1,u,v;i<n;i++){
		cin>>u>>v;
		to[u].eb(v,i);
		to[v].eb(u,i);
		e[i]={u,v};
	}
	get_d();
	int now=d2;
	while(now){
		PII tp=ff[now];
		is[now]=1;
		pos[++tot]=now;
		bl[tp.second]=d1;
		now=tp.first;
	}
	for(int i=1;i<=tot;i++) g[pos[i]]=max(i-1,tot-i);
	MS(dis,0);
	dfs2(d1,0,d1);
	for(int i=1;i<n;i++){
		if(bl[i]!=d1){
			int u=e[i].first,v=e[i].second;
			if(Dis[u]>Dis[v]) swap(u,v);
			ans[i]=g[bl[i]]+Dis[u]+dis[u];
		}
		else ans[i]=tot-1;
	}
	for(int i=1;i<n;i++) cout<<ans[i]<<"\n";
	return 0;
}

T2

给出一个 \(n\) 个点,\(m\) 条边的无向图。接下来 \(Q\) 次询问,每次从图中删掉一个点 \(A\) 后,请问此时点 \(B\) 到点 \(C\) 的最短路长度。
每次询问独立。

Floyd + 分治 + 离线。

\(solve(l,r)\) 表示不经过 \(l \sim r\) 这些点的最短路。当 \(l=r\) 时即为删去 \(l\) 点时的答案。

具体的在递归进 \(solve(mid+1,r)\) 时要先将 \(l \sim mid\) 的点处理了。

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof x)
#define MC(x,y) memcpy(x,y,sizeof x)
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define lb(x) (x&-x)
using namespace std;
const int N=200+5,M=1e5+5;
const ll INF=1e9,mod=998244353;
int n,m,Q;
ll f[N][N][N],ans[M];
struct Qry{
	int u,v,id;
};
vector<Qry> q[N];
void solve(int l,int r,int d){
	if(l==r){
		for(Qry tp:q[l]) ans[tp.id]=f[d-1][tp.u][tp.v];
		return ;
	}
	int mid=(l+r)>>1;
	MC(f[d],f[d-1]);
	for(int k=mid+1;k<=r;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				f[d][i][j]=min(f[d][i][j],f[d][i][k]+f[d][k][j]);
			}
		}
	}
	solve(l,mid,d+1);
	MC(f[d],f[d-1]);
	for(int k=l;k<=mid;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				f[d][i][j]=min(f[d][i][j],f[d][i][k]+f[d][k][j]);
			}
		}		
	}
	solve(mid+1,r,d+1);
}
int main(){
	IOS;cin>>n>>m>>Q;
	for(int i=0;i<=n+1;i++){
		for(int j=0;j<=n+1;j++){
			for(int k=0;k<=n+1;k++) f[i][j][k]=INF;
		}
	}
	for(int i=1,u,v;i<=m;i++){
		ll w;cin>>u>>v>>w;
		f[0][u][v]=f[0][v][u]=min(f[0][u][v],w);
	}
	for(int t=1,x,y,z;t<=Q;t++){
		cin>>x>>y>>z;
		q[x].pb({y,z,t});
	}
	solve(1,n,1);
	for(int i=1;i<=Q;i++) cout<<(ans[i]==INF?-1:ans[i])<<"\n";
	return 0;
}


T3

数轴上有 \(n\) 个点,第 \(i\) 个点的位置为 \(p_i\),权值为 \(a_i\)
有以下两种操作:

  1. \(A~~l~~r~~c\)\(\forall i \in [l, r],\ a_i \gets a_i + c\)
  2. \(B~~x~~y\):将数组 \(a_{\min(x,y)\sim \max(x,y)}\)\(x\) 循环移动 \(1\) 位。
    即假设 \(a_{\min(x,y)\sim \max(x,y)}=[1,2,3,4,5]\),若 \(x=5,y=1\),数组将变为 \([5,1,2,3,4]\);若 \(x=1,y=5\),数组变为 \([2,3,4,5,1]\)

最开始和每次操作后求 \(P\) 满足 \(\sum\limits_{i=1}^n a_i\times|p_i - P|\) 最小,并输出 \(P\)

FHQ_Treap 板题,但考试时没时间写了。

维护区间加,区间翻转。

\(x,y\) 的循环位移可以拆成两个区间翻转。

设权值和为 \(s\),答案为平衡树上二分找到第一个点 \(x\) 满足 $ \sum \limits_{i=1}^{x} val_i> \lfloor \frac{s+1}{2} \rfloor$ 的 \(p_x\)

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll __int128
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof x)
#define MC(x,y) memcpy(x,y,sizeof x)
#define int ll
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define lb(x) (x&-x)
using namespace std;
const int N=1e5+5,M=1e5+5;
const ll INF=1ll<<60,mod=998244353;
namespace slow{
	char Buf[1<<23],*P1=Buf,*P2=Buf;
	#define getchar() (P1==P2&&(P2=(P1=Buf)+fread(Buf,1,1<<23,stdin),P1==P2)?EOF:*P1++)
	#define T template<typename type>
	#define Ts template<typename type,typename... args>
	T inline void read(type &x){
		x=0;bool f=false;char ch=getchar();
		while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
		while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
		if(f) x=-x;
	}
	T inline void write(type o){
	    if(o<0) putchar('-'),o=-o;
	    if(o>9) write(o/10);putchar(o%10+'0');
	}
	Ts inline void read(type &x,args&... y){read(x),read(y...);}
	Ts inline void write(type x,args... y){write(x),putchar(' '),write(y...);putchar('\n');}	
}using namespace slow;
struct Tree{
	int l,r;
	ll siz,val,swp,pri,add,pre;
	void tag(int d){
		val+=d;add+=d;pre+=siz*d;
	}
	#define l(p) t[p].l
	#define r(p) t[p].r
	#define siz(p) t[p].siz
	#define val(p) t[p].val
	#define swp(p) t[p].swp
	#define pri(p) t[p].pri
	#define add(p) t[p].add
	#define pre(p) t[p].pre
}t[N];
int n,m,cnt;
int root,roota,rootb,rootc;
struct FHQ_Treap{
	int newnode(int x){
		t[++cnt]={0,0,1,x,0,rand(),0,x};
		return cnt;
	}
	void up(int p){
		if(!p) return ;
		siz(p)=siz(l(p))+siz(r(p))+1;
		pre(p)=val(p)+pre(l(p))+pre(r(p));
	}
	void down(int p){
		if(swp(p)){
			swap(l(p),r(p));
			if(l(p)) swp(l(p))^=1;
			if(r(p)) swp(r(p))^=1;
			swp(p)=0;
		}
		if(add(p)){
			if(l(p)) t[l(p)].tag(add(p));
			if(r(p)) t[r(p)].tag(add(p));
			add(p)=0;
		}
	}
	void split(int p,int x,int &L,int &R){
		if(!p) return L=R=0,void();
		down(p);
		if(siz(l(p))<x) L=p,split(r(p),x-siz(l(p))-1,r(p),R);
		else R=p,split(l(p),x,L,l(p));
		up(p);
	}
	int merge(int x,int y){
		if(!x || !y) return x|y;
		down(x),down(y);
		if(pri(x)<pri(y)) return r(x)=merge(r(x),y),up(x),x;
		else return l(y)=merge(x,l(y)),up(y),y;
	}
	void update(int l,int r,ll d){
		split(root,r,roota,rootb);
		split(roota,l-1,roota,rootc);
		t[rootc].tag(d);
		roota=merge(roota,rootc);
		root=merge(roota,rootb);
	}
	void Reverse(int l,int r){
		split(root,r,roota,rootb);
		split(roota,l-1,roota,rootc);
		swp(rootc)^=1;
		roota=merge(roota,rootc);
		root=merge(roota,rootb);		
	}
	void out(int p){
		if(!p) return ;
		down(p);
		out(l(p));
		write(val(p)),puts(" ");
		out(r(p));
	}
	int query(ll x){
		int p=root,ans=0;
		while(p){
			down(p);
			if(!l(p)){
				ans++;
				if(val(p)>=x) return ans;
				x-=val(p),p=r(p);
			}
			else if(pre(l(p))>=x) p=l(p);
			else if(pre(l(p))+val(p)>=x) return ans+siz(l(p))+1;
			else ans+=siz(l(p))+1,x-=pre(l(p))+val(p),p=r(p);
		}
		return ans;
	}
}tr;
ll pos[N];
void out(int p){
	cout<<"out************\n";
	tr.out(p);
	cout<<"\n************\n";
}
signed main(){
	read(n,m);
	for(int i=1,x;i<=n;i++){
		read(x);
		root=tr.merge(root,tr.newnode(x));
	}
	for(int i=1;i<=n;i++) read(pos[i]);
	write(pos[tr.query((pre(root)+1)/2)]);puts("");
	for(int i=1,l,r,c;i<=m;i++){
		char op=getchar();
		read(l,r);
		if(op=='A'){
			read(c);
			tr.update(l,r,c);
		}
		else{
			if(l<r) tr.Reverse(l,r),tr.Reverse(l,r-1);
			else tr.Reverse(r,l),tr.Reverse(r+1,l);
		}
		write(pos[tr.query((pre(root)+1)/2)]);puts("");
	}
	return 0;
}


T4

神秘题,不会。

10.30 NOIP 模拟赛 十六

100+100+0+0=200 pts,rk2

CSP-S 前最后一场模拟赛,膜拜 bbbzzx rk1。
T1,T2全做过原题,感觉 CSP-S 要爆炸了怎么办。

T1

lg-P9188

简单 dp,但容易写假。

\(f_i\) 表示以 \(i\) 结尾子串总和,找到最右边的 \(j\) 满足 \(s_{j\sim i}\) 是包含 bessie 的。

\(f_i=f_{j-1}+j\)

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof x)
#define MC(x,y) memcpy(x,y,sizeof x)
#define PLL pair<ll,ll>
#define lb(x) (x&-x)
using namespace std;
const int N=3e5+5,M=1e5+5;
const ll INF=1ll<<60,mod=998244353;
int n;
ll f[N],pre[N][30];
char a[N];
int get(int id){
    return pre[pre[pre[pre[pre[pre[id][5]][9]][19]][19]][5]][2];
}
int main(){
    IOS;cin>>(a+1);n=strlen(a+1);
	for(int i=1;i<=n;i++){
        MC(pre[i+1],pre[i]);
        pre[i+1][a[i]-'a'+1]=i;
    }
    for(int i=1;i<=n;i++){
        ll j=get(i+1);
        if(j) f[i]=f[j-1]+j;
    }
    ll ans=0;
    for(int i=1;i<=n;i++) ans+=f[i];
    cout<<ans<<"\n";
    return 0;
}

T2

lg-P8907

启发式合并,将 \(i\) 连的边转移到编号最小的点,延迟连边。

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof x)
#define MC(x,y) memcpy(x,y,sizeof x)
#define PLL pair<ll,ll>
using namespace std;
const int N=2e5+5,M=1e5+5;
const ll INF=1ll<<60,mod=998244353;
int n,m;
set<int> s[N];
int main(){
	IOS;cin>>n>>m;
	for(int i=1,u,v;i<=m;i++){
		cin>>u>>v;
		if(u>v) swap(u,v);
		s[u].insert(v);
	}
	ll ans=-m;
	for(int i=1;i<=n;i++){;
		if(s[i].empty()) continue;
		ans+=s[i].size();
		int u=*s[i].begin();s[i].erase(s[i].begin());
		if(s[i].size()>s[u].size()) swap(s[i],s[u]);
		for(int x:s[i]) s[u].insert(x);
	}
	cout<<ans<<"\n";
	return 0;
}

T3

lg-P8906

想到 meet in the middle,但以为复杂度不正确,就没往后面想了。

正解:

反过来改成加边。用 meet in the middle 后只用考虑其中一部分。

维护恰好经过两条边的最短路 \(d_{i,j}\)

分类讨论,若加边 \((u,v,w)\),起点为 \(s\)

  • \(k=1\):当且仅当 \(u=s\) 会贡献到答案里。
  • \(k=2\):可以计算:\(d_{i,v}=\min(d_{i,v},d_{i,u}+w)\)\(d_{u,i}\) 同理。在枚举答案 \(ans_i=\min(ans_i,d_{s,i})\)
  • \(k=3\):会发现 \((u,v,w)\)\(u=s\) 的只有 \(n\) 个,可以 \(n^2\) 转移,其余可以 \(\mathcal{O(n)}\) 转移。
  • \(k=4\):同 \(k=3\)

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof x)
#define MC(x,y) memcpy(x,y,sizeof x)
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define lb(x) (x&-x)
using namespace std;
const int N=300+5,M=N*N+5,P=11;
const ll INF=1e9+7,mod=998244353;
int n,m,k,dis[N][N];
ll ans[M];
bool vis[N][N];
struct edge{
	int u,v;
}e[M];
struct node{
	ll s,k,to[N][N],d[N][N],ans[M];
	void init(int x,int K){
		s=x;k=K;
		MS(to,0x3f);MS(d,0x3f);MS(ans,0x3f);
	}
	void insert(int u,int v,int w){
		to[u][v]=w;
		if(k==1){
			if(s==u) ans[v]=w;
			return ;
		}
		for(int i=1;i<=n;i++){
			d[i][v]=min(d[i][v],to[i][u]+w);
			d[u][i]=min(d[u][i],to[v][i]+w);
		}
		if(k==2){
			for(int i=1;i<=n;i++) ans[i]=min(ans[i],d[s][i]);
			return ;
		}
		if(k==3){
			if(s==u){
				for(int i=1;i<=n;i++){
					for(int j=1;j<=n;j++){
						ans[j]=min(ans[j],d[s][i]+to[i][j]);
					}
				}
			}
			else{
				for(int i=1;i<=n;i++){
					ans[i]=min({ans[i],to[s][u]+d[u][i],d[s][v]+to[v][i]});
					ans[v]=min(ans[v],d[s][i]+to[i][v]);
					ans[u]=min(ans[u],d[s][i]+to[i][u]);
				}
			}
			return ;
		}
		if(k==4){
			if(s==u){
				for(int i=1;i<=n;i++){
					for(int j=1;j<=n;j++){
						ans[j]=min(ans[j],d[s][i]+d[i][j]);
					}
				}
			}
			else{
				for(int i=1;i<=n;i++){
					ans[i]=min({ans[i],d[s][u]+d[u][i],d[s][v]+d[v][i]});
					ans[v]=min(ans[v],d[s][i]+d[i][v]);
					ans[u]=min(ans[u],d[s][i]+d[i][u]);
				}
			}
			return ;
		}		
	}
}s,t;
int main(){
	IOS;cin>>n>>k;m=n*n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++) cin>>dis[i][j];
	}
	for(int i=1;i<=m;i++) cin>>e[i].u>>e[i].v;
	s.init(1,k/2);t.init(n,k-k/2);
	for(int tt=m;tt>=1;tt--){
		ans[tt]=INF;
		for(int i=1;i<=n;i++) ans[tt]=min(ans[tt],s.ans[i]+t.ans[i]);
		int u=e[tt].u,v=e[tt].v,w=dis[u][v];
		s.insert(u,v,w);
		t.insert(v,u,w);
	}
	for(int i=1;i<=m;i++) cout<<(ans[i]>=INF?-1:ans[i])<<"\n";
	return 0;
}

T4

lg-P9112

黑题不会

posted @ 2025-10-28 14:02  tyh_27  阅读(1)  评论(0)    收藏  举报