版权声明: https://blog.csdn.net/qq_40828060/article/details/82983136
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;
}