20240810

赛时得分

题目 A B C D 总分 排名 比例
满分 100 100 100 100 400 165 100%
得分 40 0 41 0 81 127 77.0%

A. 括号翻转(80/100)

\(\text{80%}\) 得分做法,遇到这种括号匹配题直接用一个 stack 统计左括号位置,压入进去,当找到最近的右括号时,我们就把栈顶的左括号坐标拉出来和当前右括号坐标组成一个 pair 压入一个 vector 中。然后对于每一对括号,我们模拟把他所包含的字符串反转,这里要记住,reverse 函数是可以完成一个串内的部分反转的。如果我们想反转一个串 \(s\)\(l\)\(r\) 的部分,我们只需要调用 reverse(s.begin()+l,s.begin()+r+1) 即可。最后我们遍历一遍串,遇到括号不输出,就结束了。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
string s;
vector<pair<ll,ll> > v;
stack<ll> st;
int main()
{
	freopen("bracket.in","r",stdin);
	freopen("bracket.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>s;
	for(ll i=0;i<s.length();i++)
	{
		if(s[i]=='(') st.push(i);
		else if(s[i]==')')
		{
			v.push_back({st.top(),i});
			st.pop();
		}
	}
	for(auto i:v) reverse(s.begin()+i.first+1,s.begin()+i.second);
	for(int i=0;i<s.length();i++)
	{
		if(s[i]=='(' or s[i]==')') continue;
		else cout<<s[i];
	}
	return 0;
}

B. 相交(100/100)

赛时 sb 到连这个 20 分都写寄了。\(\text{20%}\) 得分做法,一条链的特性,实际上直接判断两个区间有没有交点即可。结果我修改 \(\max\)\(\min\) 值的时候忘记拉第三方了。

\(\text{50%}\) 得分做法,由于树上两点之间只有一条路径,我们进行一个 dfs,利用一个 stack 维护出经过的所有点的编号,扔到两个 set 中对比一下就可以了。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=1e5+1;
ll n,u,v,a,b,c,d,q,s,t;
vector<ll> e[N];
stack<ll> st,ins;
set<ll> s1,s2;
bool ok,pd1=1;
void dfs1(ll x,ll fa)
{
	st.push(x);
	if(x==b)
	{
		ins=st;
		while(!st.empty())
		{
			s1.insert(st.top());
			st.pop();
		}
		st=ins;
	}
	for(auto y:e[x])
	{
		if(y!=fa) dfs1(y,x);
	}
	st.pop();
	return;
}
void dfs2(ll x,ll fa)
{
	st.push(x);
	if(x==d)
	{
		ins=st;
		while(!st.empty())
		{
			s2.insert(st.top());
			st.pop();
		}
		st=ins;
	}
	for(auto y:e[x])
	{
		if(y!=fa) dfs2(y,x);
	}
	st.pop();
	return;
}
void sub()
{
	cin>>q;
	while(q--)
	{
		ll aa,bb,cc,dd;
		cin>>a>>b>>c>>d;
		aa=min(a,b),bb=max(a,b);
		cc=min(c,d),dd=max(c,d);
		if(cc>bb or dd<aa) cout<<"NO"<<endl;
		else cout<<"YES"<<endl;
	}
	return;
}
int main()
{
	freopen("ant.in","r",stdin);
	freopen("ant.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n;
	for(int i=1;i<n;i++)
	{
		cin>>u>>v;
		e[u].push_back(v);
		e[v].push_back(u);
		if(min(u,v)!=i or max(u,v)!=i+1) pd1=0;
	}
	if(pd1==1)
	{
		sub();
		return 0;
	}
	cin>>q;
	while(q--)
	{
		cin>>a>>b>>c>>d;
		ok=0,s1.clear(),s2.clear();
		dfs1(a,0);
		dfs2(c,0);
		for(auto i:s1)
		{
			if(s2.find(i)!=s2.end())
			{
				ok=1;
				break;
			}
		}
		if(ok==0) cout<<"NO"<<endl;
		else cout<<"YES"<<endl;
	}
	return 0; 
} 

\(\text{100%}\) 得分做法,考虑经典问题,想判断树上两条路径 \(p_1,p_2\) 是否相交,我们需要求得 \(p_1,p_2\)\(lca_1,lca_2\),若 \(lca_1\)\(p_2\) 上或 \(lca_2\)\(p_1\) 上,那么两条路径就是相交的。那么,想判断一个点 \(x\) 是否在以 \(s,t\) 为起点终点的一条路径上,我们只需要判断 \(dis_{x,s}+dis_{x,t}\) 是否等于 \(dis_{s,t}\) 即可,其中 \(dis\) 表示树上两点间距离。

我们用 LCA 求得两点间距离,即可 \(\text{O(1)}\) 进行查询。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=2e5+1;
ll n,q,u,v,a,b,s,t,d[N],p[N][21],lca1,lca2;
vector<ll> e[N];
void dfs(ll x,ll fa)
{
	d[x]=d[fa]+1;
	p[x][0]=fa;
	for(int i=1;(1<<i)<=d[x];i++) p[x][i]=p[p[x][i-1]][i-1];
	for(auto y:e[x])
	{
		if(y!=fa) dfs(y,x);
	}
	return;
}
ll lca(ll a,ll b)
{
	if(d[a]>d[b]) swap(a,b);
	for(int i=20;i>=0;i--)
	{
		if(d[a]<=d[b]-(1<<i)) b=p[b][i];
	}
	if(a==b) return a;
	for(int i=20;i>=0;i--)
	{
		if(p[a][i]==p[b][i]) continue;
		else a=p[a][i],b=p[b][i];
	}
	return p[a][0];
}
ll getdis(ll s,ll t)
{
	return d[s]+d[t]-2*d[lca(s,t)];
}
bool pd(ll x,ll s,ll t)
{
	if(getdis(s,x)+getdis(x,t)==getdis(s,t)) return 1;
	else return 0;
}
int main()
{
	freopen("ant.in","r",stdin);
	freopen("ant.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n;
	for(int i=1;i<n;i++)
	{
		cin>>u>>v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	dfs(1,0);
	cin>>q;
	while(q--)
	{
		cin>>a>>b>>s>>t;
		lca1=lca(a,b),lca2=lca(s,t);
		if(pd(lca1,s,t)==1 or pd(lca2,a,b)==1) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
	return 0;
}

C. 排列盒子(41/100)

\(\text{41%}\) 得分做法,首先统计原序列中各元素的数量,然后用 next_permutation 枚举全排列,再将排列中元素个数变成统计的数量,也就可以得到所有的答案序列。对于每一种答案序列我们从左到右枚举处理,每处理完一个序列我们就将他放在前面,统计一下所有处理过程中的答案最小值即可。

#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=1e5+1;
ll n,k,f[N],pos,cnt1,cnt2,cnt[N],a[N];
ll ans=2147483648,stp,p[N],x,ins[N],res,ins2[N],cntt;
void sub1()
{
	string ak="";
	for(int i=1;i<=n;i++)
	{
		cin>>f[i];
		if(f[i]==1) ak+="1";
		else ak+="2"; 
	}
	for(int i=0;i<ak.length();i++)
	{
		if(ak[i]=='1')
		{
			cnt1=cnt1+(i-pos);
			pos++;
		}
	}
	pos=0;
	for(int i=0;i<ak.length();i++)
	{
		if(ak[i]=='2')
		{
			cnt2=cnt2+(i-pos);
			pos++;
		}
	}
	cout<<min(cnt1,cnt2);
	return;
}
int main()
{
	freopen("box.in","r",stdin);
	freopen("box.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>k;
	if(k==2)
	{
		sub1();
		return 0;
	}
	for(int i=1;i<=n;i++)
	{
		cin>>f[i];
		cnt[f[i]]++;
	}
	for(int i=1;i<=k;i++) a[i]=i;
	do
	{
		stp=0,x=1,res=1,cntt=0;
		memset(p,0,sizeof(p));
		for(int i=1;i<=n;i++) ins[i]=f[i];
		for(int i=1;i<=k;i++)
		{
			for(int j=1;j<=cnt[a[i]];j++) p[x++]=a[i];
		}
		for(int j=1;j<=k;j++)
		{
			for(int i=1;i<=n;i++)
			{
				if(ins[i]==a[j])
				{
					stp+=(i-res);
					res++;
				}
			}
			cntt=0;
			memset(ins2,0,sizeof(ins2));
			for(int i=1;i<=cnt[a[j]];i++) ins2[++cntt]=a[j];
			for(int i=1;i<=n;i++)
			{
				if(ins[i]!=a[j]) ins2[++cntt]=ins[i];
			}
			for(int i=1;i<=n;i++) ins[i]=ins2[i];
		}
		ans=min(ans,stp);
	}while(next_permutation(a+1,a+k+1));
	cout<<ans;
	return 0;
}
posted @ 2024-08-10 14:26  Lithium_Chestnut  阅读(7)  评论(0)    收藏  举报