CSP-S复赛讲解

1.[CSP-S 2024] 决斗

简述题意

每个怪兽有一个实力 riri​,每个怪兽只可以欺负实力严格小于自己的怪兽,被成功欺负的怪兽出局。当未退出游戏的怪兽都已发起过攻击时,游戏结束。规定一种方案使得未退出游戏的怪兽数量尽可能少。

算法分析

显然,对于 a<ri,b<ria<ri​,b<ri​,a,ba,b 在 riri​ 面前是等价的(都能被欺负)。

定义 sumksumk​ 表示 ri=kri​=k 的怪兽个数,cntcnt 为对于实力为 [1,i−1][1,i−1] 的怪兽的最优解。

所以按实力从小到大欺负,有两种情况:

  • 如当前 sumk≤cntsumk​≤cnt,欺负 sumksumk​ 只即可。即 cnt←cntcnt←cnt。
  • 否则,欺负当前所有怪兽,即 cnt←sumkcnt←sumk​。

上面的过程可看做 cnt=maxsumk

然后这题就做完了。

诶等等,如果这个数据:

3
1 2 2

有一只怪兽好像没有欺负别人啊?

其实让剩下相同的再互相欺负一遍即可。


时间复杂度 O(n+A),A 为 ri 的值域。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[100005];
signed main()
{
	int n,x,ans=0;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int x;
		cin>>x;
		a[x]++;
	}
	for(int i=1;i<=100000;i++) ans=max(ans,a[i]);
	cout<<ans;
	return 0;
}

2.[CSP-S 2024] 超速检测

解题思路

感觉写完三个特殊性质就会满分了。

由于初始速度,加速度均确定,那么容易看出对于每辆汽车,他们的行驶速度都是单调不增或单调不降的。

因此容易得出超速的区间是连续的一段区间。

因此我们可以二分出是否有合法的区间。

这样就完成了第一问。

考虑第二问,我们发现可以把超速的这个区间转化成可以检测出这个汽车超速的左右端点。

发现我们可以再次二分得出这段区间的左右端点。

此时第二问就转化成了有 nn 条线段,至少选择即可端点才能使得每段区间都至少包含一个点。

这个问题类似于线段覆盖。

我们考虑先将右端点从小到大排序,然后如果此时线段内没有端点,那么我们就可以选取这个右端点,发现这样一定是最优的,于是我们依次选取右端点优先队列维护即可。

#include<bits/stdc++.h>
using namespace std;
//#define map unordered_map
#define re register
#define ll long long
#define forl(i,a,b) for(re ll (i)=(a);i<=(b);(i)++)
#define forr(i,a,b) for(re ll (i)=(a);i>=(b);(i)--)
#define forll(i,a,b,c) for(re ll (i)=(a);i<=(b);(i)+=(c))
#define forrr(i,a,b,c) for(re ll (i)=(a);i>=(b);(i)-=(c))
#define forL(i,a,b,c) for(re ll (i)=(a);((i)<=(b)) && (c);(i)++)
#define forR(i,a,b,c) for(re ll (i)=(a);((i)>=(b)) && (c);(i)--)
#define forLL(i,a,b,c,d) for(re ll (i)=(a);((i)<=(b)) && (d);(i)+=(c))
#define forRR(i,a,b,c,d) for(re ll (i)=(a);((i)>=(b)) && (d);(i)-=(c))
#define pii pair<ll,ll>
#define mid ((l+r)>>1)
#define lowbit(x) (x&-x)
#define pb push_back
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define endl '\n'
#define QwQ return 0;
#define db long double
#define ull unsigned long long
#define lcm(x,y) (1ll*(x)/__gcd(x,y)*(y))
#define Sum(x,y) (1ll*((x)+(y))*((y)-(x)+1)/2)
#define x first
#define y second
#define aty cout<<"Yes\n";
#define atn cout<<"No\n";
#define cfy cout<<"YES\n";
#define cfn cout<<"NO\n";
#define xxy cout<<"yes\n";
#define xxn cout<<"no\n";
#define printcf(x) x?cout<<"YES\n":cout<<"NO\n";
#define printat(x) x?cout<<"Yes\n":cout<<"No\n";
#define printxx(x) x?cout<<"yes\n":cout<<"no\n";
#define maxqueue priority_queue<ll>
#define minqueue priority_queue<ll,vector<ll>,greater<ll>>
#define bug cout<<"---------------------------------------\n";
//ll pw(ll x,ll y,ll mod){if(y==0)return 1;if(x==0)return 0;ll an=1,tmp=x;while(y){if(y&1)an=(an*tmp)%mod;tmp=(tmp*tmp)%mod;y>>=1;}return an;}
template<typename T1,typename T2>bool Max(T1&x,T2 y){if(y>x)return x=y,1;return 0;}
template<typename T1,typename T2>bool Min(T1&x,T2 y){if(y<x)return x=y,1;return 0;}
ll _t_;
void _clear(){}
ll n,m,len,V;
ll l[100010],r[100010];
ll d[100010],v[100010],a[100010];
ll p[100010];
ll ans1,ans2;
__int128 V2;
priority_queue<pii,vector<pii>,greater<pii>>q;
__int128 f(__int128 v0,__int128 a,__int128 s){
	return v0*v0+2*a*s;
}
void solve()
{
	ans1=ans2=0;
    _clear();
	while(!q.empty())
		q.pop();
	cin>>n>>m>>len>>V;
	V2=V;
	V2*=V;
	forl(i,1,n)
		cin>>d[i]>>v[i]>>a[i];
	forl(i,1,m)
		cin>>p[i];
		
	forl(i,1,n)
	{
		if(a[i]==0)
		{
			ll L=1,R=m;
			while(L<R)
			{
				ll Mid=(L+R)/2;
				if(p[Mid]<d[i])
					L=Mid+1;
				else
					R=Mid;		
			}
			if(p[L]<d[i] || v[i]<=V)
			{
				l[i]=r[i]=-1;
				continue;
			}
			l[i]=L;
			r[i]=m;
		}
		else if(a[i]<0)
		{
			ll L=d[i],R=len;
			while(L<R)
			{
				ll Mid=(L+R+1)/2;
				if(f(v[i],a[i],Mid-d[i])<=V2)
					R=Mid-1;
				else
					L=Mid;
			}
//			cout<<"["<<L<<','<<R<<"]\n";
			if(f(v[i],a[i],L-d[i])<=V2)
			{
				l[i]=r[i]=-1;
				continue;
			}
			ll ansr=L;
			ll ansl=d[i];
//			cout<<"["<<ansl<<','<<ansr<<"]\n";
			L=1,R=m;
			while(L<R)
			{
				ll Mid=(L+R)/2;
				if(p[Mid]<ansl)
					L=Mid+1;
				else
					R=Mid;			
			}
			if(p[L]<ansl || p[L]>ansr)
			{
				l[i]=r[i]=-1;
				continue;
			}
			l[i]=L;
			
			L=1,R=m;
			while(L<R)
			{
				ll Mid=(L+R+1)/2;
				if(p[Mid]>ansr)
					R=Mid-1;
				else
					L=Mid;
			}
			r[i]=L;
		}
		else
		{
			ll L=d[i],R=len;
			while(L<R)
			{
				ll Mid=(L+R)/2;
				if(f(v[i],a[i],Mid-d[i])<=V2)
					L=Mid+1;
				else
					R=Mid;
			}
			if(f(v[i],a[i],L-d[i])<=V2)
			{
				l[i]=r[i]=-1;
				continue;
			}
			ll ansl=L;
			ll ansr=len;

			L=1,R=m;
			while(L<R)
			{
				ll Mid=(L+R)/2;
				if(p[Mid]<ansl)
					L=Mid+1;
				else
					R=Mid;			
			}
			if(p[L]<ansl || p[L]>ansr)
			{
				l[i]=r[i]=-1;
				continue;
			}
			l[i]=L;
			
			L=1,R=m;
			while(L<R)
			{
				ll Mid=(L+R+1)/2;
				if(p[Mid]>ansr)
					R=Mid-1;
				else
					L=Mid;
			}
			r[i]=L;			
		}
	}
//	forl(i,1,n)
///		cout<<l[i]<<' '<<r[i]<<endl;
//		
	forl(i,1,n)
		if(l[i]!=-1)
			q.push({r[i],l[i]}),
			ans1++;
	ll nr=-1;
	while(!q.empty())
	{
		pii now=q.top();
		q.pop();
		if(nr<now.y)
			nr=now.x,
			ans2++;
	}
	cout<<ans1<<' '<<m-ans2<<endl;
}
int main()
{
//    freopen("tst.txt","r",stdin);
//    freopen("sans.txt","w",stdout);
    IOS;
    _t_=1;
    cin>>_t_;
    while(_t_--)
        solve();
    QwQ;
}

3.[CSP-S 2024] 染色

解题思路

设 dpi,jdpi,j​ 表示考虑前 ii 个数,最后一个数涂的颜色是 jj(j∈{0,1})的最大得分。

对于第 ii 位,它可能产生贡献,也可能不产生贡献。

如果它没有贡献,直接 dpi,0=dpi,1=max⁡(dpi−1,0,dpi−1,1)。

如果它有贡献,以 dpi,0 为例:

那么前面一定也有一个染成了颜色 00 的相同的数,且这之间所有数都染成了颜色 11。

最优显然要选择前面最后一个相同的数,因为如果不是最后一个,可以在这中间所有染成 11 的数里面找到一个相同的数,把它也染 00 收益更大。

设这个数的位置为 lsti,那么 dpi,0=ai+dplsti+1,1+slsti+1,i−1dpi,其中 sl,r表示将 [l,r]染成一个颜色会产生的收益,可以用前缀和搞,这样转移是 O(1)的。dpi,1同理。当然要特判一下 ai=ai−1的情况。

这两个取 max⁡ 即可。最后输出 max⁡(dpi,0,dpi,1)。

其实 dpi,0 应该等于 dpi,1的来着,没必要开两倍空间,不过考场上也没想着优化了,反正能过就行。

#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define Add(x, y) (x + y >= mod) ? (x + y - mod) : (x + y)
#define lowbit(x) x & (-x)
#define pi pair<ll, ll>
#define pii pair<ll, pair<ll, ll>>
#define iip pair<pair<ll, ll>, ll>
#define ppii pair<pair<ll, ll>, pair<ll, ll>>
#define ls(k) k << 1
#define rs(k) k << 1 | 1
#define fi first
#define se second
#define full(l, r, x) for(auto it = l; it != r; ++it) (*it) = x
#define Full(a) memset(a, 0, sizeof(a))
#define open(s1, s2) freopen(s1, "r", stdin), freopen(s2, "w", stdout);
#define For(i, l, r) for(register int i = l; i <= r; ++i)
#define _For(i, l, r) for(register int i = r; i >= l; --i)
using namespace std;
using namespace __gnu_pbds;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const int N = 2e5 + 10, M = 1e6 + 10;
inline ll read(){
    ll x = 0, f = 1;
    char c = getchar();
    while(c < '0' || c > '9'){
        if(c == '-')
          f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    return x * f;
}
inline void write(ll x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)
	  write(x / 10);
	putchar(x % 10 + '0');
}
int T, n;
ll pre;
ll a[N], s[N], dp[N], t[N], Max[M];
namespace Tree{
	ll a[M], b[M];
	void init(){
		for(int i = 0; i < M; ++i)
		  a[i] = b[i] = -1e18;
	}
	void add(int x, ll v){
		for(int i = x; i < M; i += lowbit(i))
		  a[i] = max(a[i], v);
		x = M - x - 1;
		for(int i = x; i < M; i += lowbit(i))
		  b[i] = max(b[i], v);
	}
	ll query(int x){
		ll ans = -1e18;
		for(int i = x - 1; i > 0; i -= lowbit(i))
		  ans = max(ans, a[i]);
		x = M - x - 2;
		for(int i = x; i > 0; i -= lowbit(i))
		  ans = max(ans, b[i]);
		return ans;
	}
};
inline ll get(int x, int y){
	if(!x || !y)
	  return 0;
	if(a[x] == a[y])
	  return a[x];
	return 0;
}
void solve(){
	memset(dp, -0x7f, sizeof(dp));
	memset(t, -0x7f, sizeof(t));
	memset(Max, -0x7f, sizeof(Max));
	n = read();
	for(int i = 1; i <= n; ++i){
		a[i] = read();
		s[i] = s[i - 1] + get(i - 1, i);
	}
	Tree::init();
	dp[1] = pre = 0;
	for(int i = 2; i <= n; ++i){
		t[i] = dp[i] = pre + get(i - 2, i - 1);
		pre = dp[i];
		dp[i] = max(dp[i], s[i - 1] + a[i] + Max[a[i]]);
//		cerr << Max[a[i]] << '\n';
		dp[i] = max(dp[i], s[i - 1] + Tree::query(a[i]));
		Max[a[i - 1]] = max(Max[a[i - 1]], dp[i] - s[i]);
		Tree::add(a[i - 1], dp[i] - s[i]);
		dp[i] = max(dp[i], get(i - 1, i) + dp[i - 1]);
	}
	write(dp[n]);
	putchar('\n');
}
bool End;
int main(){
//	open("A.in", "A.out");
	T = read();
	while(T--)
	  solve();
	cerr << '\n' << abs(&Begin - &End) / 1048576 << "MB";
	return 0;
}

4.[CSP-S 2024] 擂台游戏

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100001;
int n,m,k;
int a[N];
int c[N];
char d[N*2];
int f[N*4],g[N*4],h[N*4],b[N*4];
void dfs(int i,int t){
	f[i]=-1,g[i]=t+1;
	if(i>=(1<<k))return;
	int j=(i<<1)+d[i]-'0';
	h[j]=h[j^1]=h[i]-1;
	if(b[i])b[j]=b[j^1]=b[i];
	else b[j]=h[i],b[j^1]=0;
	dfs(j,t),dfs(j^1,t);
}
void upd(int i,int t){
	if(!i)return;
	if(f[i]!=-1)return;
	int j=(i<<1)+d[i]-'0';
	if(f[j]>=h[i])f[i]=f[j],g[j^1]=min(g[j^1],t);
	else if(f[j]!=-1)f[i]=f[j^1];
	if(f[j]!=-1)upd(i>>1,t);
}
void get(int i){
	if(i>=(1<<k))return;
	int j=(i<<1)+d[i]-'0';
	g[j]=min(g[j],g[i]);
	g[j^1]=min(g[j^1],g[i]);
	get(j),get(j^1);
}
ll s[N*2],res[N*2];
void solve(int t){
	int p=1<<(k-t);
	h[p]=t,b[p]=0;
	dfs(p,1<<t);
	for(int i=1;i<=n;i++){
		int j=i+(1<<k)-1;
		if(a[i]<b[j])g[j]=min(g[j],i);
		f[j]=a[i];
		upd(j>>1,i);
	}
	get(p);
	for(int i=1;i<=(1<<t);i++)s[i]=0;
	for(int i=1;i<=(1<<t);i++){
		int j=i+(1<<k)-1;
		s[g[j]-1]+=i;
	}
	for(int i=(1<<t);i>=1;i--){
		s[i-1]+=s[i];
		res[i]=s[i];
	}
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	while((1<<k)<n)k++;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=m;i++)cin>>c[i];
	for(int i=k-1;i>=1;i--)
		for(int j=0;j<(1<<i);j++)
			cin>>d[(1<<i)+j];
	cin>>d[1];
	int T;
	cin>>T;
	while(T--){
		int x[4];
		cin>>x[0]>>x[1]>>x[2]>>x[3];
		for(int i=1;i<=n;i++)a[i]^=x[i%4];
		for(int i=k;i>=0;i--)solve(i);
		ll ans=0;
		for(int i=1;i<=m;i++)ans^=i*res[c[i]];
		cout<<ans<<'\n';
		for(int i=1;i<=n;i++)a[i]^=x[i%4];
	}
	return 0;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值