暑假集训CSP提高模拟3
2/35 反向挂了若干分又正向挂了若干
T1 abc猜想
水,随便变形推个柿子糊个快速幂就好了
T2 简单的排列最优化题
考虑只计算每次右移的 \(delta\),我们发现一个点只会在到贡献为 \(0\) 的位置和序列开头会改变对 \(delta\) 的贡献,直接算就好,\(O(n)\) 的
T3 简单的线性做法题
难题,赛时写暴力套了个卡时怒艹 \(85\)
考虑两个部分分之间可以根号分治。
再考虑一个分治,直接从分治中心暴力扩展,两边扫一遍统计出可能的值(可以证明整个区间的绝对众数一定等于两边某一前(后)缀的众数)
枚举这个可能的答案,可以证明只有 \(O(\log n)\) 个,之后开个桶,记录每个右区间可以和几个左区间拼合就好了,需要前缀和优化,加上分治,复杂度 \(O(n \log^2 n)\),不过貌似有线性做法
贴个代码
#include<bits/stdc++.h>
#define llt long long
const llt N=1001000;
using namespace std;
llt n,s[N],bin[N],us[N],ans;vector<llt> vec;bool vis[N];
void solve(llt l,llt r)
{
if(l==r) {ans++;return;}
vector<llt>().swap(vec);
llt mid=l+r>>1,best=0,len;
for(int i=mid;i>=l;i--)
{
bin[s[i]]++;
if(bin[s[i]]>bin[best]) best=s[i];
if(vis[best]==0) vec.push_back(best),vis[best]=1;
}
for(int i=mid;i>=l;i--) bin[s[i]]--;
for(int i=mid+1;i<=r;i++)
{
bin[s[i]]++;
if(bin[s[i]]>bin[best]) best=s[i];
if(vis[best]==0) vec.push_back(best),vis[best]=1;
}
for(int i=mid+1;i<=r;i++) bin[s[i]]--;
len=vec.size();
for(int j=0;j<len;j++)
{
vis[vec[j]]=0;
for(int i=mid;i>=l;i--)
bin[s[i]]++,us[bin[vec[j]]*2-mid+i-1+mid-l+1]++;
for(int i=(mid-l+1)*2;i>=0;i--) us[i]+=us[i+1];
for(int i=mid;i>=l;i--) bin[s[i]]--;
for(int i=mid+1;i<=r;i++) bin[s[i]]++,ans+=us[(i-mid)-2*bin[vec[j]]+mid-l+1+1];
for(int i=mid+1;i<=r;i++) bin[s[i]]--;
for(int i=(mid-l+1)*2;i>=0;i--) us[i]=0;
}
solve(l,mid);solve(mid+1,r);
}
int main()
{
#ifdef LOCAL
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&s[i]);
solve(1,n);
printf("%lld\n",ans);
return 0;
}
T4 简单的线性做法题
简单题,考虑每个数最多只会被开 \(6\) 次,暴力修改即可,可以用势能线段树或者并查集找到区间第一个可以修改的值# 暑假集训CSP提高模拟3
2/35 反向挂了若干分又正向挂了若干
T1 abc猜想
水,随便变形推个柿子糊个快速幂就好了
T2 简单的排列最优化题
考虑只计算每次右移的 \(delta\),我们发现一个点只会在到贡献为 \(0\) 的位置和序列开头会改变对 \(delta\) 的贡献,直接算就好,\(O(n)\) 的
T3 简单的线性做法题
难题,赛时写暴力套了个卡时怒艹 \(85\)
考虑两个部分分之间可以根号分治。
再考虑一个分治,直接从分治中心暴力扩展,两边扫一遍统计出可能的值(可以证明整个区间的绝对众数一定等于两边某一前(后)缀的众数)
枚举这个可能的答案,可以证明只有 \(O(\log n)\) 个,之后开个桶,记录每个右区间可以和几个左区间拼合就好了,需要前缀和优化,加上分治,复杂度 \(O(n \log^2 n)\),不过貌似有线性做法
贴个代码
#include<bits/stdc++.h>
#define llt long long
const llt N=1001000;
using namespace std;
llt n,s[N],bin[N],us[N],ans;vector<llt> vec;bool vis[N];
void solve(llt l,llt r)
{
if(l==r) {ans++;return;}
vector<llt>().swap(vec);
llt mid=l+r>>1,best=0,len;
for(int i=mid;i>=l;i--)
{
bin[s[i]]++;
if(bin[s[i]]>bin[best]) best=s[i];
if(vis[best]==0) vec.push_back(best),vis[best]=1;
}
for(int i=mid;i>=l;i--) bin[s[i]]--;
for(int i=mid+1;i<=r;i++)
{
bin[s[i]]++;
if(bin[s[i]]>bin[best]) best=s[i];
if(vis[best]==0) vec.push_back(best),vis[best]=1;
}
for(int i=mid+1;i<=r;i++) bin[s[i]]--;
len=vec.size();
for(int j=0;j<len;j++)
{
vis[vec[j]]=0;
for(int i=mid;i>=l;i--)
bin[s[i]]++,us[bin[vec[j]]*2-mid+i-1+mid-l+1]++;
for(int i=(mid-l+1)*2;i>=0;i--) us[i]+=us[i+1];
for(int i=mid;i>=l;i--) bin[s[i]]--;
for(int i=mid+1;i<=r;i++) bin[s[i]]++,ans+=us[(i-mid)-2*bin[vec[j]]+mid-l+1+1];
for(int i=mid+1;i<=r;i++) bin[s[i]]--;
for(int i=(mid-l+1)*2;i>=0;i--) us[i]=0;
}
solve(l,mid);solve(mid+1,r);
}
int main()
{
#ifdef LOCAL
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&s[i]);
solve(1,n);
printf("%lld\n",ans);
return 0;
}
T4 简单的线性做法题
简单题,考虑每个数最多只会被开 \(6\) 次,暴力修改即可,可以用势能线段树或者并查集找到区间第一个可以修改的值,赛时被卡常了
标签:bin,int,mid,集训,--,暑假,llt,CSP,best From: https://www.cnblogs.com/hzoi-wang54321/p/18321715