20181008模拟赛 赛后处刑

Lozinke

为什么会做这么一道题呢?

当然是昨天老师突然想让我们打一场%你赛,只能恭敬不如从命了…

以下是我的考场代码,因为写的是brute force所以直接strstr判断的子串,最差复杂度O(n^2),结果加了乱七八糟一堆剪枝和排序优化吸一口氧竟然过了76分…

虽然没A掉,但也相当于把刚学会的map set给完全的巩固了一下,至少知道怎么愉快的写暴力了

//对于n^2暴力的多个优化
//1、重复的串可以一起统计
//2、去重后相同长度的串不可能存在包含关系
//3、长度大的串不可能被小的包含
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<set>
int const maxn=20100,maxm=111,inf=0x1f1f1f1f;
std::string s[maxn];
int anss,n,cnt;
std::map<std::string,int>ton;
std::map<int,int>lenton;
std::map<std::string,int>ans;
struct cmp
{
	std::string s;
	int operator <(const cmp &x)const
	{
		if(x.s.size()==s.size())
			return x.s>s;
			//这里涉及一个机制
			//set的去重是去掉不大于不小于的值
			//而我是按串长度进行排序的,如果不特判一下,就会把长度相同的干掉
		return x.s.size()>s.size();
	}
}b;
std::set<cmp>ss;

void read()
{
	std::cin>>n;
	for(int i=1;i<=n;i++)
	{
		std::cin>>b.s;
		if(!ton[b.s])
//		{
			ss.insert(b);
//			std::cout<<b.s<<std::endl;
//		}
		ton[b.s]++;
	}
}
int main()
{
//	freopen("lozinke.in","r",stdin);
//	freopen("lozinke.out","w",stdout);
	std::ios::sync_with_stdio(false);
	read();
//	for(std::set<cmp>::iterator sit=ss.begin();sit!=ss.end();sit++)
//		std::cout<<(*sit).s<<std::endl;
	for(std::set<cmp>::iterator sit=ss.begin();sit!=ss.end();sit++)
	{
		std::string ss1,ss2;
		ss1=(*sit).s;
		const char *s1=ss1.c_str();
		std::set<cmp>::iterator j=sit;
		for(j++;j!=ss.end();j++)
		{
			ss2=(*j).s;
			if(ss1.size()==ss2.size())
				continue;
			const char *s2=ss2.c_str();
			if(strstr(s2,s1))
				ans[ss1]+=ton[ss2];
		}
	}
	for(std::set<cmp>::iterator sit=ss.begin();sit!=ss.end();sit++)
		anss+=ans[(*sit).s]*ton[(*sit).s]+ton[(*sit).s]*ton[(*sit).s]-ton[(*sit).s];
	printf("%d",anss);
	return 0;
}

这里是AC代码,思路来自考完试后的solution…

之前那个代码完全没用上字符串<=10的性质…

事实上只需要枚举所有子串就好了…

最后看看每个串出现过几次,减掉把自己枚举上的的就ok辣

复杂度O(100*nlogn)

log来自于慢的一匹的set

顺便说一下为什么要用set记录子串

因为要去重啊!

比如aaa如果不去重,所有子串就是a,aa,aaa,a,aa

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<set>
int const maxn=20100,maxm=111,inf=0x1f1f1f1f;
std::string s[maxn];
int anss,n;
std::map<std::string,int>ans;
std::set<std::string>ss;  
int main()
{
//	freopen("lozinke.in","r",stdin);
//	freopen("lozinke.out","w",stdout);
	std::ios::sync_with_stdio(false);
	std::cin>>n;
	for(int i=1;i<=n;i++)
	{
		ss.clear();
		std::cin>>s[i];
		for(int j=0;j<s[i].size();j++)
		{
			std::string s2;
			for(int k=j;k<s[i].size();k++)
			{
				s2.push_back(s[i][k]);
				ss.insert(s2);
			}
		}
		for(std::set<std::string>::iterator sit=ss.begin();sit!=ss.end();sit++)
			ans[*sit]++;
	}
	for(int i=1;i<=n;i++)
		anss+=ans[s[i]];
	printf("%d",anss-n);
	return 0;
}

Deda

有大佬说std是竞赛树,然而并不会
这道题乍一看要维护两个东西,实际上我们可以把其中一个看做序列的下标,另一个看成某一区间的性质,因为是最值,所以具有区间可合并性(前提是此区间符合查询中的要求),用线段树就很显然了

那么到底以哪个量为下标建立序列呢?
都行,只不过我比较懒,觉得离线+离散化怪麻烦就选了年龄(事实上站点更好理解)

每个叶子节点作为年龄,维护该年龄最早下车的站点min,每次查询返回答案结点的序列左端点即可

细节见代码

#include<iostream>
#include<cstdio>
typedef long long lll;
int const inf=0x1f1f1f1f,maxn=1000110;
char op;
int age;lll sta;
int n,q;
int cnt=1,tot;
struct Tree
{
	int lc,rc;
	lll min;
}a[maxn<<2];

inline void pushup(int u)
{
	a[u].min=std::min(a[a[u].lc].min,a[a[u].rc].min);
}
inline void build(int u,int l,int r)
{
	if(l==r)
	{
		a[u].min=1e18;
		return;
	}
	int mid=(l+r)/2;
	a[u].min=1e18;
	a[u].lc=++cnt;
	build(a[u].lc,l,mid);
	a[u].rc=++cnt;
	build(a[u].rc,mid+1,r);
}
inline void update(int u,int l,int r,int age,lll sta)
{
	if(l==r&&l==age)
	{
		a[u].min=sta;
		return;
	}
	int mid=(l+r)/2;
	if(mid<age)
		update(a[u].rc,mid+1,r,age,sta);
	else
		update(a[u].lc,l,mid,age,sta);
	pushup(u);
}
inline int query(int u,int l,int r,int ll,int rr,lll sta)
{
	if(a[u].min>sta)
		return inf;
		//不符合题意,最早下车的站点都比询问大,显然不需要继续向下找了
	if(l==ll&&l==rr)
		return l;
	int mid=(l+r)/2;
	if(ll>mid)
		return query(a[u].rc,mid+1,r,ll,rr,sta);
	else
		if(rr<=mid)
			return query(a[u].lc,l,mid,ll,rr,sta);
		else
		{
			int res=query(a[u].lc,l,mid,ll,mid,sta);
			//注意,如果左区间符合题意,那答案一定在左区间,因为左区间比右区间优
			if(res!=inf)
				return res;
			return query(a[u].rc,mid+1,r,mid+1,rr,sta);
		}
}
int main()
{
	scanf("%d%d",&n,&q);
	build(1,1,n);
	for(int i=1;i<=q;i++)
	{
		scanf("%s%lld%d",&op,&sta,&age);
		if(op=='M')
			update(1,1,n,age,sta);
		else
		{
			int ans=query(1,1,n,age,n,sta);
			if(ans==inf)
			{
				printf("-1\n");
				continue;
			}
			printf("%d\n",ans);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值