• Educational Codeforces Round 110 (Rated for Div. 2)


    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);
    }
    
  • 相关阅读:
    Javascript DOM 编程艺术读书笔记16/03/25
    2014 Multi-University Contest 1.1 hdu4861 打表找规律
    汇编小记16/3/23
    汇编小记16/3/22
    hdoj 4940 强连通图
    Head FIRST HTML & CSS 16/03/17
    html&css一些有用的网站整理
    dosbox+debug 模拟dos
    汇编小记16/3/15
    解决windwos另存为,保存文件时无法选择“桌面”文件夹
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14876760.html
Copyright © 2020-2023  润新知