CF1922B & C & D

CF1922B

分析

注意到 \(2^0+2^1<2^2\),因此若 \(a_i\ne a_j\ne a_k\),这组数就是不合法的,所以题目转化为有多少对三元组 \(i,j,k\) 使得 \(a_i,a_j,a_k\) 中至少有两个数相等。考虑分类讨论。

第一类,\(a_i,a_j,a_k\) 中有两个数相等,不妨设 \(a_i=a_j\),那么先开一个 map 维护所有 \(a_i\) 的出现次数,即 \(mp_i\) 表示 \(i\)\(a\) 中出现次数。则对于每一个 \(mp_i>1\),它对第一类答案的贡献为 \(C_{mp_i}^2\times (n-mp_i)\),也就是从 \(mp_i\) 个数里面选择 \(2\) 个数,再从剩下的数里选择 \(1\) 个。

第二类,\(a_i=a_j=a_k\),那么不难想到贡献是 \(C_{mp_i}^3\)

AC Code

#include <bits/stdc++.h>
using namespace std;
#define int long long
int a[300010];
map<int,int> mp;
set<int> st;
signed main()
{
	int t;
	cin>>t;
	while(t--)
	{
		mp.clear();
		st.clear();
		int n,ans=0;
		cin>>n;
		for(int i=1;i<=n;i++) cin>>a[i],mp[a[i]]++,st.insert(a[i]);
		int tp=0;
		for(auto i:st)
		{
			if(mp[i]<2)
			{
				tp+=mp[i];
				continue;
			}
			if(mp[i]==2) ans+=mp[i]*(mp[i]-1)/2*tp;
			else ans+=mp[i]*(mp[i]-1)/2*tp+mp[i]*(mp[i]-1)*(mp[i]-2)/6;
			tp+=mp[i];
		}
		cout<<ans<<endl;
	}
	return 0;
}

CF1922C

分析

注意到从 \(1\sim n\) 的旅游费用单调递增,从 \(n\sim 1\) 的旅游费用也是单调递增,考虑前缀和维护。先对每个城市维护一下最近的城市,再从 \(1\sim n\)\(n\sim1\) 两个方向扫一遍,就得到了两个 \(sum\) 数组(前缀和数组),记为 \(sum_1,sum_2\),对于每次询问,若 \(x<y\),输出 \({sum_1}_y-{sum_1}_x\),否则输出 \({sum_2}_y-{sum_2}_x\)

AC Code

#include <bits/stdc++.h>
using namespace std;
#define int long long
int a[100010],sum1[100010],sum2[100010],nc[100010];
void solve()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	nc[1]=2;
	nc[n]=n-1;
	for(int i=2;i<n;i++)
	nc[i]=(abs(a[i]-a[i-1])>abs(a[i]-a[i+1])?i+1:i-1);
	for(int i=2;i<=n;i++) sum1[i]=sum1[i-1]+(nc[i-1]==i?1:abs(a[i-1]-a[i]));
	for(int i=n-1;i;i--) sum2[i]=sum2[i+1]+(nc[i+1]==i?1:abs(a[i+1]-a[i]));
	int q;
	cin>>q;
	while(q--)
	{
		int a,b;
		cin>>a>>b;
		if(a<b) cout<<sum1[b]-sum1[a]<<endl;
		else cout<<sum2[b]-sum2[a]<<endl;
	}
}
signed main()
{
	int t;
	cin>>t;
	while(t--) solve();
	return 0;
}

CF1922D

分析

赛时时间少,未 AC。赛后经过大佬 happybob 指导才过了。

如果一个怪死了,受影响的只有它周围的两个怪,因此考虑开两个 set,分别为 \(st_1,st_2\),记录着活着的怪与该回合下来将死的怪。第一轮前先把所有 \(1\sim n\) 的数加入 \(st_1\),把受到伤害大于护盾值的加入 \(st_2\)。每回合进行的时候,将所有死亡的怪左右的怪(前提是它们活着)加入一个 vector 中,这些怪是受到了影响的怪,若它们受到的伤害大于护盾值,加入 \(st_2\)

考虑如何维护 \(st_1,st_2\)。回合进行的时候,发现如果一连串怪都死了,和它们一个一个死没有区别,所以每死一个怪,将它左右两边(也就是在 \(st_1\) 中它的位置的前一个元素与后一个元素)的怪受到的伤害更改,然后在 \(st_1\) 中删除自己,这样如果接下来死了一个相邻的怪,它照样可以影响到之前的怪的相邻两边。

综上,最后的答案是 \(st_2\) 的大小。

最后,多测不清空,爆零两行泪;清空用 memset,TLE 两行泪。

AC Code

#include <bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int a[N],d[N],nd[N];
void solve()
{
	set<int> st1,st2;
	int n;
	cin>>n;
	a[n+1]=0;
	for(int i=1;i<=n;i++) cin>>a[i],st1.insert(i);
	for(int i=1;i<=n;i++) cin>>d[i];
	for(int i=1;i<=n;i++) nd[i]=a[i-1]+a[i+1];
	vector<int> v;
	for(int i=1;i<=n;i++) if(nd[i]>d[i]) st2.insert(i);
	for(int i=1;i<=n;i++)
	{
		v.clear();
		int res=st2.size();
		for(auto&j:st2)
		{
			auto it1=st1.find(j),it2=it1;
			if(it2!=--st1.end()) it2++;
			if(it1!=st1.begin())
			{
				it1--;
				int idx=*it1;
				nd[idx]-=a[j];
				if(j!=*it2) nd[idx]+=a[*it2];
			}
			if(*it2!=j)
			{
				nd[*it2]-=a[j];
				if(*it1!=j)
				nd[*it2]+=a[*it1];
			}
			if(!st2.count(*it1)) v.push_back(*it1);
			if(!st2.count(*it2)) v.push_back(*it2);
			st1.erase(st1.find(j));
		}
		st2.clear();
		for(auto&j:v)
		{
			if(nd[j]>d[j])
			st2.insert(j);
		}
		cout<<res<<' ';
	}
	cout<<endl;
}
int main()
{
	int t;
	cin>>t;
	while(t--) solve();
	return 0;
} 
posted @ 2024-01-20 10:59  Crazyouth  阅读(20)  评论(0编辑  收藏  举报