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;
}