CF1939C More Gifts 题解
XVIII Open Olympiad in Informatics - Final Stage, Day 1 T3 非官方题解。
赛时没反应过来基环树找环,惨痛
以下所有内容均默认数组下标从
令
先考虑怎样的分配礼物策略是最优的:显然是对于每个人能拿就拿。于是对于每个 std::set 简单地预处理。
但这样还不够。于是对于每个
于是令
考虑优化上述的过程。我们发现
可以借助 std::set 与 std::map 较为简单地实现
放代码:
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace std;
struct Hash{
static uint64_t splitmix64(uint64_t x){
x+=0x9e3779b97f4a7c15;
x=(x^x>>30)*0xbf58476d1ce4e5b9;
x=(x^x>>27)*0x94d049bb133111eb;
return x^x>>31;
}
size_t operator()(uint64_t x)const{
static const uint64_t FIXED_RANDOM=chrono::steady_clock::now().time_since_epoch().count();
return splitmix64(x+FIXED_RANDOM);
}
};
// 哈希函数,可以参照:https://codeforces.com/blog/entry/62393
int main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n,k,t; long long c=0; cin>>n>>k>>t;
vector<int> a(n);
__gnu_pbds::gp_hash_table<int,int,Hash> m;
for(auto &i:a)
if(cin>>i;m.find(i)==m.end())m[i]=c++;
for(auto &i:a)i=m[i]; // 离散化
if(c<=t)cout<<"1\n",exit(0);
vector<int> nx(n),d(n),f(n),s(n);
vector<vector<int> > g(n);
for(int i=0,l=0,e=0;i<n;i++){
while(1){
if(++s[a[l%n]]==1)e++;
if(e>t){
nx[i]=l,s[a[l%n]]=0,e--;
if(l<n)g[l].emplace_back(i);
break;
} // 拿的礼物种类 > t
l++;
}
if(!--s[a[i]])e--;
} // 预处理 nx[i]
queue<int> q;
for(int i=n-1;i>=0;i--)
if(nx[i]>=n)q.emplace(f[i]=i);
while(!q.empty()){
int u=q.front(); q.pop();
for(int i:g[u])
f[i]=f[u],d[i]=d[u]+1,q.emplace(i);
} // 预处理 f[i] 与 d[i]
vector<int> w(n<<1),b(n,-1);
for(int i=c=0,p=0;i<k;i++){
if(~b[p]){
int x=(k-b[p])/(i-b[p]),r=(k-b[p])%(i-b[p]);
c+=(c-w[b[p]]+d[p])*(x-1);
for(int j=0;j<r;j++)
c+=d[p],p=nx[f[p]]-n;
break;
} // 找到环
w[b[p]=i]=(c+=d[p]),p=nx[f[p]]-n;
} // 基环树找环
cout<<c+k<<endl;
return 0;
}