【洛谷】NOIP2018原创模拟赛DAY2题解

版权声明:欢迎转载+原文章地址~ https://blog.csdn.net/Hi_KER/article/details/82820861

前言:

我相信大家可以感觉到DAY2题目的难度明显比DAY1大很多,这也是近年NOIP考试的趋势,从目前NOIP考察的知识来看这次的T3知识可能对NOIP选手来说略难,但说不定今年NOIP还会考更高级的算法,所以要有所防备。

再说一点,大家一定要注意部分分的获取。对于这套题,如果能力一般的同学采用部分分算法,理论至少上可以得到:100(预处理+动态规划)+40(只解决纯串联或并联情况)+55(采用暴力的“逐步爬山法”计算)=195分,对于DAY2来说是一个不错的分数。对于T2,个人觉得细节相当多,而且不知道“标程”是否正确。对于T3,得部分分难度不大,但它就是一个防NOIP选手AK的题,可以说,对大部分人(包括我)来说这道题写正解(200+ 行代码)还不如暴力(约100行代码)来得快。

最后,不要认为出题人能力比其他人强。因为出题没有比赛那么短的时间限制,所以可以花更多时间来解决自己的题。比如对于T2的第一个和第二个版本我自己都做不来,于是改为了现在的第三个版本,而且“标程”写错两次;对于T3我自己写了大约3个小时才写完正解。所以这次比赛200+的同学都是能力在我之上的。                               ——于2018/10/14

当然题目方法不唯一,如果大家对DAY2的题目有更好的解法,欢迎提出!

比赛链接:NOIP2018原创模拟赛DAY2


T1:最后的战役

考察知识:map,动态规划

算法难度:XXX+ 实现难度:XXX

说实话,我动态规划很弱,所以就出了一道不那么难的动态规划。

这道题还有更快的方法,请参考:洛谷 U40593 最后的战役(思维+离散化)

这道题贪心并不是完美解法,但是数据为随机生成的,所以得分概率非常大。

Hack数据:

4 2
1 1
2 3
3 6
4 8
1 2 3 4

标程输出:22 贪心输出:21

分析:

首先,我们要解决操作2,如果暴力枚举时间复杂度为O(n^2)

其实吧,我们直接用map优化就可以了,时间复杂度:O(nlogn)

    for(int i=1;i<=n;i++) P[i]=max(P[i-1],p[i]);//求最大值
    for(int i=1;i<=n;i++){
    	mp[k[i]]+=p[i];//在这里求和
        P[i]=max(mp[x[i]],P[i]);
    }

在处理了伏地魔在 [1,n] 每一步可以获得的最大魔法能量之后(记为P [ i ],记\sum P[i] 为 SUM),我们就可以采取动态规划了:

定义:f(i,j)表示在[ 1 , i ] 秒中在 i 秒使用了魔法,且有 j 个时间段使用了魔法,可以得到的最大能量值

边界:f(i,1)=SUM-P[i]+P[i+1]

状态转移:f(i,j)=max\left \{ f(k,j-1)-P[i]+P[i+1] \right \}\,\,\,\, 0<k<i-1

状态转移方程的实现还是要稍微处理一下,直接实现会超时:

    for(int i=1;i<=n;i++)//处理边界
        f[i][1]=SUM-P[i]+P[i+1],
        ans=max(ans,f[i][1]);
    for(int j=2;j<=m;j++){
        for(int k=1;k<=n;k++) T[k]=max(T[k-1],f[k][j-1]);//预处理
        for(int i=2*j-1;i<=n;i++)
            f[i][j]=T[i-2]-P[i]+P[i+1],
            ans=max(ans,f[i][j]);
    }

好了,这道题我们就做出来了,理论时间复杂度:O(nlogn+nm),但是时间效率不是很高,面对最大数据需要近900ms。

T2:流量计算

考察知识:图论,数学,推导,搜索

算法难度:XXXX 实现难度:XXXX

出题背景:我目前在学高中电学。最开始这道题搞得很难,我自己都做不来,于是不断降低难度:从混连->并联与并联和串联嵌套->并联只能嵌套串联。

分析:

这道题需要大家有一定的电学知识和数学推导能力。

先推导几个电学结论:

结论一:对于串联电路,我们可以将所有电阻R_1,R_2,...,R_n看作一个等效电阻:R_{\sum}=\sum_{i=1}^n R_i

证明:易得,略

结论二:对于一个(等效)并联电路,电阻分别为R_1,R_2,...,R_n等效电阻为:R_{\sum}=\frac{1}{\sum_{i=1}^n \frac{1}{R_i}}

证明:设电压为 U,由欧姆定律,总电流为:I_{\sum}=\sum_{i=1}^n \frac{U}{R_i},故等效电阻为:R_{\sum}=\frac{U}{I_{\sum}}=\frac{1}{\sum_{i=1}^n \frac{1}{R_i}}

结论三:对于一个(等效)并联电路,电阻分别为R_1,R_2,...,R_n,我们可以采取下面的算法计算等效电阻:

double R_=R[1];
for(int i=2;i<=n;i++) R_=R_*R[i]/(R_+R[i]);
ans=R_;

证明:结论二的二维形式:R_{1,2}=\frac{R_1R_2}{R_1+R_2},每次将两个电阻合并为一个等效电阻,最后的值即为总等效电阻

结论四:对于一个(等效)并联电路,电阻分别为R_1,R_2,...,R_n,其支路最小电流为:\frac{R_{\sum}I_{\sum}}{max\{R_i\}}

证明:我们知道,最小电流在电阻最大的支路上,即max\{R_i\},而电压为R_{\sum}I_{\sum},由I=\frac{U}{R},就得到结论

有了这几个结论,我们就可以开始动手了:

1.先用结论三处理重边

2.然后bfs求出由电源正极到负极的一条路径

3.以这条路径为主线开始处理,遇到分叉边就表明此处有并联嵌套串联,用结论一+dfs计算嵌套串联的等效电阻,并记下k=min\{k,\frac{R_{\sum}}{max\{R_i\}}\},然后利用结论三进行并联电阻的合并

4.将所有等效电阻求和,即为整个电路的总等效电阻,如果主线电流为 I 则最小电流 I_{min}=kI

代码实现细节请参考代码

T3:PION后缀自动机

考察知识:树链剖分,线段树,链表,排序,二分,字符串处理

算法难度:XXXXX 实现难度:XXXXX

分析:难题,算法复杂,代码量大!这是一道典型的考场上正解不如写暴力的题!

先介绍暴力算法(55分):

如果你的程序超时,你可以先尝试这道题:PION后缀自动机(数据弱化版)

算法难度:XXX 实现难度:XXX+

显然储存文件后缀信息不能用数组,空间开不下,所以我们用链表储存所有文件夹中文件的后缀信息;

然后是后缀的处理,注意到字符串长度小于6,如果我们用 1 表示 a ,2 表示 b ,... ... ,26 表示 z,这就相当于将字符串看做一个27进制数,这样我们就可以在 int 范围内用数字表示字符串了

解决了上面两个问题之后,我们就可以建树然后对每个操作用暴力的 “逐步爬山法” 解决了,实现难度不大

其实这种方法对随机数据效果还是比较好的(甚至比下面的满分算法还略快一点),但是你觉得数据可能完全随机么?

时间复杂度:极端情况约 O(mn)

满分算法:

代码量比较大。

看到这道题我们应该可以想到用树链剖分来做。

我们先考虑怎么对序列进行统计和修改,对于操作2,我们需要找到一个序列中的子序列。我们可以用一个结构体来储存,结构体储存序列中每个元素的值(字符串的hash值)和序列中每个元素在序列中的位置。然后将这个结构体序列按元素值的排序(元素值相同的按在序列中的位置排序)。当我们需要查找一个序列相同数值所在的区间的时候用 lower_bound 和 upper_bound 就可以完成,对于查找出来的目标区间,还需要用二分法来查找该区间中元素的下标在目标子序列中的数量。

至于修改,用线段树的区间维护实现。

解决了对序列的修改,我们还要解决树中每个节点含有的文件数目不相同的问题,如果一个节点没有文件,那么简单的方法是新建一个伪文件,可以将hash值赋为 -1。

之后就是树链剖分了,不同于树链剖分模板,我们还要新建一个辅助数组 low[i] ,表示节点 i 的重儿子的重儿子...(以此类推)可以到达的最深节点的编号,我们先对[\,top[i],low[i]\,]排序,之后查询的时候我们查询 [\,top[i],low[i]\,] 之间的序列即可。

其实吧,上面只是代码实现的一部分,具体细节远不止这些,更多的细节请参考代码。

时间复杂度:极端情况约 O((n+m)log^2n)


代码:

(仅供参考)

T1:

#include<cstdio>
#include<cstring>
#include<map>
#include<algorithm>
using namespace std;
const int maxn=50005;
map<int,int>mp;
int k[maxn],p[maxn],x[maxn];
int n,m,P[maxn],f[maxn][505],T[maxn],sum[maxn],SUM;
void ready(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d%d",k+i,p+i);
    for(int i=1;i<=n;i++) scanf("%d",x+i);
    for(int i=1;i<=n;i++) P[i]=max(P[i-1],p[i]);
    for(int i=1;i<=n;i++){
    	mp[k[i]]+=p[i];//用map储存即可 
        P[i]=max(mp[x[i]],P[i]);
    }
}
void dp(){
/*
f(i,j)表示已i为结尾且有j个时间段使用了魔法,可以得到的最大能量值
f(i,j)=MAX{f(k,j-1)}-P[i]+P[i+1]  1<j<=i-2 
*/ 
    int ans;
    for(int i=1;i<=n;i++) SUM+=P[i];
    ans=SUM;
    if(m==0) {printf("%d\n",ans);return;}
    for(int i=1;i<=n;i++)
        f[i][1]=SUM-P[i]+P[i+1],
        ans=max(ans,f[i][1]);
    for(int j=2;j<=m;j++){
        for(int k=1;k<=n;k++) T[k]=max(T[k-1],f[k][j-1]);//类似于预处理 
        for(int i=2*j-1;i<=n;i++)
            f[i][j]=T[i-2]-P[i]+P[i+1],
            ans=max(ans,f[i][j]);
    }
    printf("%d\n",ans);
}
int main(){
    ready();
    dp();
    return 0;
}

T2:

#include<map>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=20005;
map<pair<int,int>,double>mp,mp2;
map<pair<int,int>,double>::iterator it;
struct edge{
	int to,next;
	double R;
}e[maxn*5];
int head[maxn],np;
void adde(int u,int v,double R){
	e[++np]=(edge){v,head[u],R};
	head[u]=np;
	e[++np]=(edge){u,head[v],R};
	head[v]=np;
}
int n,m,from,to;
bool vis[maxn],done1[maxn],done2[maxn];
int d[maxn],fa[maxn],son[maxn];
double U,k=1.0,R_sum,R_fa[maxn],T[maxn];
void build(){
	int R,u,v;
	char S[5];
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d%s%d",&u,&v,S,&R);
		if(S[0]=='P') from=u,to=v,U=(double)R;//找到电源 
		else{
			if(u>v) swap(u,v);
			pair<int,int>pr=make_pair(u,v);
			if(mp.count(pr)){//处理重边构成的并联
				double R_=mp[pr];
				mp[pr]=R_*R/(R+R_);//并联电路电阻的计算 
				mp2[pr]=max(mp2[pr],(double)R);
			}
			else mp[pr]=mp2[pr]=(double)R;
		}
	}
	for(it=mp.begin();it!=mp.end();it++){
		pair<int,int>pr=it->first;
		k=min(k,mp[pr]/mp2[pr]);//k的计算 
		adde(pr.first,pr.second,it->second);
	}
}
void bfs(int s){//寻找路径 
	memset(d,0x3f,sizeof(d));
	memset(vis,0,sizeof(vis));
	fa[s]=d[s]=0,vis[s]=true;
	queue<int>q;
	q.push(s);
	while(!q.empty()){
		int i=q.front();q.pop();
		for(int p=head[i];p;p=e[p].next){
			int j=e[p].to;
			if(vis[j]) continue;
			fa[j]=i,R_fa[j]=e[p].R;
			vis[j]=true,d[j]=d[i]+1;
			q.push(j);
		}
	}
}
int dfs(int i,double& sum){//寻找支路 
	for(int p=head[i];p;p=e[p].next){
		int j=e[p].to;
		if(done2[j]) continue;
		sum+=e[p].R;
		if(done1[j]) return j;//找到另一个交汇点 
		else {done2[j]=true;return dfs(j,sum);}
	} return -1;
}
void solve(){
	bfs(from);
	for(int i=to;i;i=fa[i]) done1[i]=true,son[fa[i]]=i;//标记主线 
	for(int i=to;i!=from;i=fa[i]){
		double R_max,R_=0;//R_:等效电阻 
		int cnt=0,pos;
		for(int p=head[i];p;p=e[p].next){
			int j=e[p].to;
			if(done1[j]||done2[j]) continue;
			T[++cnt]=mp[make_pair(min(i,j),max(j,i))];
			done2[i]=done2[j]=true;//标记支线 
			pos=dfs(j,T[cnt]);
		}
		if(!cnt){R_sum+=R_fa[i];continue;}//当前路线不包含并联
		for(int j=i;j!=pos;j=fa[j]) R_+=R_fa[j];
		R_max=R_;
		for(int j=1;j<=cnt;j++){
			R_max=max(R_max,T[j]);
			R_=R_*T[j]/(R_+T[j]);
		}
		k=min(k,R_/R_max);
		R_sum+=R_,i=son[pos];
	}
	printf("%.2lf\n%.2lf\n",U/R_sum,U*k/R_sum);
}
int main(){
	build();
	solve();
	return 0;
}

T3:(暴力算法)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100005;
int next[maxn*5],hash[maxn*5],first[maxn],last[maxn],np_;
void add_file(int u,int num){//链表实现按位置储存文件 
	hash[++np_]=num;
	if(first[u]) next[last[u]]=np_;
	else first[u]=np_;
	last[u]=np_;
}
int hash_num(char* s){
	int ret=0;
	for(int i=0;s[i]!=0;i++) ret=ret*27+s[i]-96;
	return ret;
}
struct edge{//储存树边 
    int to,next;
}e[maxn*2];
int head[maxn],np;
void adde(int u,int v){//添加树边 
    e[++np]=(edge){v,head[u]};
    head[u]=np;
    e[++np]=(edge){u,head[v]};
    head[v]=np;
}
int n,m,k[maxn],dep[maxn],fa[maxn];
void dfs(int i,int Fa){
	fa[i]=Fa,dep[i]=dep[Fa]+1;
	for(int p=head[i];p;p=e[p].next){
		int j=e[p].to;
		if(j==Fa) continue;
		dfs(j,i);
	}
}
void build(){
	int u,v;
	char ext_nm[10];
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++) scanf("%d%d",&u,&v),adde(u,v);
	for(int i=1;i<=n;i++){
		scanf("%d",k+i);
		for(int j=1;j<=k[i];j++){
			scanf("%s",ext_nm);
			add_file(i,hash_num(ext_nm));//插入链表 
		}
	}
	dfs(1,0);
}
int lca(int x,int y){
	while(x!=y){
		if(dep[x]<dep[y]) y=fa[y];
		else x=fa[x];
	}
	return x;
}
int query_path(int x,int y,int id,bool del){//暴力处理文件夹 
	int ret=0;
	while(x!=y){
		if(dep[x]<dep[y]) swap(x,y);
		for(int p=first[x];p;p=next[p]) if(hash[p]==id){
			ret++;
			if(del) hash[p]=-1;
		}
		x=fa[x];
	}
	for(int p=first[x];p;p=next[p])if(hash[p]==id){
		ret++;
		if(del) hash[p]=-1;
	}
	return ret;
}
void solve(){
	char cmd[3][10];
	int u,v,id;
	while(m--){
		scanf("%s%s",cmd[0],cmd[1]);
		if(cmd[0][0]=='q'){
			if(cmd[1][1]=='p'){
				scanf("%d%d",&u,&v);
				printf("%d\n",dep[u]+dep[v]-2*dep[lca(u,v)]);
			} else {
				scanf("%d%d%s",&u,&v,cmd[2]);
				id=hash_num(cmd[2]+2);
				printf("%d\n",query_path(u,v,id,false));
			}
		}
		else{
			scanf("%d%d%s",&u,&v,cmd[2]);
			id=hash_num(cmd[2]+2);
			printf("%d\n",query_path(u,v,id,true));
		}
	}
}
int main(){
    build();
    solve();
    return 0;
}

*T3:(正解)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100005;
int next[maxn*5],hash[maxn*5],first[maxn],last[maxn],np_;
void add_file(int u,int num){//链表实现按位置储存文件 
	hash[++np_]=num;
	if(first[u]) next[last[u]]=np_;
	else first[u]=np_;
	last[u]=np_;
}
int hash_num(char* s){ 
	int ret=0;
	for(int i=0;s[i]!=0;i++) ret=ret*27+s[i]-96;
	return ret;
}
struct edge{//储存树边 
    int to,next;
}e[maxn*2];
int head[maxn],np;
void adde(int u,int v){//添加树边 
    e[++np]=(edge){v,head[u]};
    head[u]=np;
    e[++np]=(edge){u,head[v]};
    head[v]=np;
}
struct ext_name{
    int v,id;
    const bool operator < (const ext_name& B)const {
        return v<B.v;
    }
}D[maxn*6];
bool cmp(const ext_name& A,const ext_name& B){
    return A.v<B.v||(A.v==B.v&&A.id<B.id);
}
int n,m,k[maxn];
/*--------------------线段树----------------------*/
int rt,nowpos,lc[maxn*12],rc[maxn*12],sum[maxn*12],setv[maxn*12];
#define pushup(now) sum[now]=sum[lc[now]]+sum[rc[now]]
void pushdown(int now,int l,int r,int M){
	if(setv[now]!=-1) return;
	sum[lc[now]]=sum[rc[now]]=0;
	setv[lc[now]]=setv[rc[now]]=-1;
	setv[now]=0;
}
void build(int& now,int l,int r){
	now=++nowpos;
	if(l==r) {sum[now]=1;return;}
	int M=(l+r)>>1;
	build(lc[now],l,M);
	build(rc[now],M+1,r);
	pushup(now);
}
void update(int now,int l,int r,int x,int y){
	if(x<=l&&r<=y) {setv[now]=-1,sum[now]=0;return;}
	int M=(l+r)>>1;
	pushdown(now,l,r,M);
	if(y<=M) update(lc[now],l,M,x,y);
	else if(x>M) update(rc[now],M+1,r,x,y);
	else update(lc[now],l,M,x,y),update(rc[now],M+1,r,x,y);
	pushup(now);
}
int query(int now,int l,int r,int x,int y){
	if(x<=l&&r<=y) return sum[now];
	int M=(l+r)>>1;
	pushdown(now,l,r,M);
	if(y<=M) return query(lc[now],l,M,x,y);
	else if(x>M) return query(rc[now],M+1,r,x,y);
	else return query(lc[now],l,M,x,y)+query(rc[now],M+1,r,x,y);
}
/*-------------------树链剖分--------------------*/
int fa[maxn],son[maxn],dep[maxn],sz[maxn];
int P,top[maxn],seg[maxn],rev[maxn],low[maxn];
int lP[maxn],tP[maxn],pos;
void dfs1(int i,int Fa){
	dep[i]=dep[Fa]+1,fa[i]=Fa,sz[i]=1;
	for(int p=head[i];p;p=e[p].next){
		int j=e[p].to;
		if(j==Fa) continue;
		dfs1(j,i);
		sz[i]+=sz[j];
		if(sz[son[i]]<sz[j]) son[i]=j;
	}
}
void load_files(int segl,int segr){
	sort(D+lP[segl],D+tP[segr]+1,cmp);
}
void init_folder(int u){
	int i=rev[u];
	lP[u]=pos+1;
	if(k[i]){
		for(int p=first[i];p;p=next[p])
			D[++pos].v=hash[p],D[pos].id=u;
		tP[u]=pos;
	}
	else D[++pos].v=-1,D[pos].id=u,tP[u]=pos;//如果为空文件夹装载假文件 
}
void dfs2(int i){
	if(sz[i]==1)
		load_files(seg[top[i]],seg[i]),low[i]=i;//将文件排序 
	if(son[i]){
		top[son[i]]=top[i];
		seg[son[i]]=++P,rev[P]=son[i];
		init_folder(P);//将文件夹中的文件写入序列 
		dfs2(son[i]);
		low[i]=low[son[i]];
	}
	for(int p=head[i];p;p=e[p].next){
		int j=e[p].to;
		if(top[j]) continue;
		top[j]=j,seg[j]=++P,rev[P]=j;
		init_folder(P);
		dfs2(j);
	}
}
int lca(int x,int y){
	int fx=top[x],fy=top[y];
	while(fx!=fy){
		if(dep[fx]<dep[fy]) swap(fx,fy),swap(x,y);
		x=fa[fx],fx=top[x];
	}
	if(dep[x]<dep[y]) return x;
	return y;
}
int calc(int l,int r,int L__,int R__,int id,bool del){//最难懂的部分之一,用二分寻找子序列位置
	ext_name Tmp;Tmp.v=id;
	L__=lP[L__],R__=tP[R__];
	int L_=lower_bound(D+L__,D+R__+1,Tmp)-D,L;
	int R_=upper_bound(D+L__,D+R__+1,Tmp)-D,R;
	if(L_>=R_) return 0;//没有找到
	R_--;
	int l_=R_+1,r_=L_-1;
	L=L_,R=R_;
	while(L<=R){//二分寻找
		int M=(L+R)>>1;
		if(D[M].id>=l) l_=M,R=M-1;
		else L=M+1;
	}
	L=L_,R=R_;
	while(L<=R){
		int M=(L+R)>>1;
		if(D[M].id<=r) r_=M,L=M+1;
		else R=M-1;
	}
	if(l_>r_) return 0;
	int TMP=query(rt,1,pos,l_,r_);//在线段树中查询 
	if(del) update(rt,1,pos,l_,r_);//删除文件 
	return TMP;
}
int query_path(int x,int y,int id,bool Access_y){//树链剖分中跳路径的方法
	int fx=top[x],fy=top[y],ret=0;
	while(fx!=fy){
		ret+=calc(seg[fx],seg[x],seg[fx],seg[low[fx]],id,false);
		x=fa[fx],fx=top[x];
	}
	if(Access_y) ret+=calc(seg[y],seg[x],seg[top[y]],seg[low[y]],id,false);
	else ret+=calc(seg[y]+1,seg[x],seg[top[y]],seg[low[y]],id,false);
	return ret;
}
int del_path(int x,int y,int id){
	int fx=top[x],fy=top[y],ret=0;
	while(fx!=fy){
		ret+=calc(seg[fx],seg[x],seg[fx],seg[low[fx]],id,true);
		x=fa[fx],fx=top[x];
	}
	ret+=calc(seg[y],seg[x],seg[top[y]],seg[low[y]],id,true);
	return ret;
}
void build(){
	int u,v;
	char ext_nm[10];
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++) scanf("%d%d",&u,&v),adde(u,v);
	for(int i=1;i<=n;i++){
		scanf("%d",k+i);
		for(int j=1;j<=k[i];j++){
			scanf("%s",ext_nm);
			add_file(i,hash_num(ext_nm));//插入到链表 
		}
	}
	dfs1(1,0);
	seg[1]=top[1]=P=rev[1]=1;
	init_folder(1);
	dfs2(1);
	build(rt,1,pos);//初始化线段树 
}
void solve(){
	char cmd[3][10];
	int u,v,a,id;
	while(m--){
		scanf("%s%s",cmd[0],cmd[1]);
		if(cmd[0][0]=='q'){
			if(cmd[1][1]=='p'){
				scanf("%d%d",&u,&v);
				printf("%d\n",dep[u]+dep[v]-2*dep[lca(u,v)]);
			}
			else{
				scanf("%d%d%s",&u,&v,cmd[2]);
				a=lca(u,v),id=hash_num(cmd[2]+2);
				printf("%d\n",query_path(u,a,id,true)+query_path(v,a,id,false));
			}
		}
		else{
			scanf("%d%d%s",&u,&v,cmd[2]);
			a=lca(u,v),id=hash_num(cmd[2]+2);
			printf("%d\n",del_path(u,a,id)+del_path(v,a,id));
		}
	}
}
int main(){
    build();
    solve();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Hi_KER/article/details/82820861