E. Gold Transfer
题目描述
解法
贪心的看,我们一定是从最浅的祖先开始选起走的。
然后我就想到了树上前缀和,找到刚好选完的那个临界点,用倍增实现。
但是这道题的点是动态加入的,所以前缀和维护不了。有一个极好的均摊分析做法,我们每次就找到最浅的有金子的祖先,然后只考虑它这个单点,要么它被买完,要不我们的购买完成,所以总操作数是 (O(q)) 的。
那么时间复杂度 (O(qlog q)),简单倍增即可实现。
#include <cstdio>
#include <iostream>
using namespace std;
const int M = 300005;
#define int long long
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int q,a[M],c[M],fa[M][20];
void work(int x,int y)
{
int r1=0,r2=0;
while(a[x]>0 && y>0)
{
int t=x;
for(int i=19;i>=0;i--)
if(a[fa[t][i]]) t=fa[t][i];
int tmp=min(a[t],y);
a[t]-=tmp;y-=tmp;
r1+=tmp;r2+=tmp*c[t];
}
cout<<r1<<" "<<r2<<endl;
fflush(stdout);
}
signed main()
{
cin>>q>>a[1]>>c[1];
for(int n=2;n<=q+1;n++)
{
int op;cin>>op;
if(op==1)
{
cin>>fa[n][0]>>a[n]>>c[n];
fa[n][0]++;
for(int i=1;i<=19;i++)
fa[n][i]=fa[fa[n][i-1]][i-1];
}
else
{
int x,y;cin>>x>>y;x++;
work(x,y);
}
}
}
F. String Distance
题目描述
解法
我们只讨论字符集合相同的字符串,记长度为 (m),此时 (f(s,t)) 只有两种取值,讨论一下这两种情况:
- 如果某个子串 ([l,r]) 是有序的,并且两个串满足 ([1,l),(r,n]) 都相等,答案是 (1)
- 否则答案是 (2)
我们找出第一种情况个数即可,我们把同一集合里面的所有串先按字典序排序,对于区间 ([1,l)) 相同的若干个串,([l,r]) 有序的一定排在前面,所以我们可以让 (s=a_i,t=a_j(j>i)) 来统计答案。
我们固定 (s) 的某个极长有序区间,然后统计去掉这个区间后字符串相同 (t) 的个数。可以把前缀后缀的相等分开考虑,按字典序排序后前缀的变化是连续的,也就是 (a_{i+1}...a_n) 和 (a_i) 的 (lcp) 是单调不降的,并且 (a_i) 和 (a_j) 的 (lcp) 可以表示为 (min_{k=i}^{j-1}lcp(k,k+1)),所以可以维护一个单调栈,每次把栈顶 (lcp) 大于等于当且 (lcp) 的元素弹掉即可。
那么对于单调栈里面元素代表的区间 ([x,y]),(a_x...a_y) 和 (a_i) 有相同的 (lcp) 记为 (p),可以借助预处理找到极长有序区间 ((p,q]),然后找 (a_x...a_y) 和 (a_i) 满足 ((q,m]) 这个后缀相同的个数。可以把反串插入 ( t tire) 树中,每个节点上维护一个 ( t vector),定位到 ( t tire) 上面节点后二分找即可。
时间复杂度 (O(sum |s_i|cdotlog |s_i|)),代码里面用了好多 ( t vector)
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
#include <map>
using namespace std;
const int M = 200005;
#define int long long
#define pii pair<int,int>
#define make make_pair
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,k,cnt,ans,sum,ch[M][26];
vector<int> id[M],pos[M];map<string,vector<string>>mp;
//tire
void ins(string s,int x)
{
pos[x].clear();
for(int i=0;i<=m;i++) pos[x].push_back(0);
id[1].push_back(x);
pos[x][m]=1;
for(int i=m-1,p=1;i>=0;i--)
{
int c=s[i]-'a';
if(!ch[p][c]) ch[p][c]=++cnt;
p=ch[p][c];
pos[x][i]=p;
id[p].push_back(x);
}
}
int ask(int p,int l,int r)
{
return lower_bound(id[p].begin(),id[p].end(),r)-
lower_bound(id[p].begin(),id[p].end(),l);
}
//solve
int solve(vector<string> a)
{
//init
n=a.size();m=a[0].size();
sort(a.begin(),a.end());
for(int i=0;i<=cnt;i++)
{
for(int j=0;j<26;j++) ch[i][j]=0;
id[i].clear();
}
cnt=1;
for(int i=0;i<n;i++) ins(a[i],i);
//work for ordered subsequence
vector<int> dec[n];
for(int i=0;i<n;i++)
{
for(int j=1;j<m;j++)
if(a[i][j-1]>a[i][j])
dec[i].push_back(j);
dec[i].push_back(m);
}
//work for lcp
vector<int> lcp(n);
for(int i=1;i<n;i++)
{
int j=0;
while(j<m && a[i][j]==a[i-1][j]) j++;
lcp[i]=j;
}
//cal
int res=n*(n-1);
vector<pii> stk;stk.push_back(make(n,-1));
for(int i=n-1;i>=0;i--)
{
for(int j=1;j<stk.size();j++)
{
int l=stk[j].first,r=stk[j-1].first,p=stk[j].second;
p=*upper_bound(dec[i].begin(),dec[i].end(),p);
res-=ask(pos[i][p],l,r);
}
while(stk.back().second>=lcp[i]) stk.pop_back();
stk.push_back(make(i,lcp[i]));
}
return res;
}
signed main()
{
k=read();
for(int i=0;i<k;i++)
{
string s,t;
cin>>s;t=s;
sort(t.begin(),t.end());
mp[t].push_back(s);
}
for(auto it:mp)
{
vector<string> a=it.second;
ans+=1337*a.size()*sum;
ans+=solve(a);
sum+=a.size();
}
printf("%lld
",ans);
}