Educational Codeforces Round 110 (Rated for Div. 2)

E. Gold Transfer

题目描述

点此看题

解法

贪心的看,我们一定是从最浅的祖先开始选起走的。

然后我就想到了树上前缀和,找到刚好选完的那个临界点,用倍增实现。

但是这道题的点是动态加入的,所以前缀和维护不了。有一个极好的均摊分析做法,我们每次就找到最浅的有金子的祖先,然后只考虑它这个单点,要么它被买完,要不我们的购买完成,所以总操作数是 \(O(q)\) 的。

那么时间复杂度 \(O(q\log 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]\) 这个后缀相同的个数。可以把反串插入 \(\tt tire\) 树中,每个节点上维护一个 \(\tt vector\),定位到 \(\tt tire\) 上面节点后二分找即可。

时间复杂度 \(O(\sum |s_i|\cdot\log |s_i|)\)代码里面用了好多 \(\tt 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\n",ans);
}
posted @ 2021-06-11 22:56  C202044zxy  阅读(77)  评论(0编辑  收藏  举报