A:Rikka with Sequence IV
题意简述
有一个长度为
n
的数列
求想要知道满足条件的字典序最小的排列
P
。
思路
贪心。
从前往后扫,选出第一个前缀和大于0的。
这样扫n遍扫出所有的数。
复杂度
代码
#include<cstdio>
using namespace std;
int n,cnt;
long long sum;
int seq[1010],ans[1010];
bool vis[1010];
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&seq[i]);
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
if (!vis[j]&&sum+seq[j]>=0)
{
sum+=seq[j];
ans[++cnt]=j;
vis[j]=1;
break;
}
if (cnt!=i)
{
printf("Impossible");
return 0;
}
}
for (int i=1;i<=n;i++)
printf("%d ",ans[i]);
return 0;
}
B:Rikka with Subsequence
题意简述
有一个长度为
n
的字符串
Note:字符串
s
是字符串
思路
这个人还没有做过
题解见这个人的题解
代码
坑
C:Rikka with String
题意简述
有一个长度为n的只包含小写字母的字符串s,现在可以选取一个位置
k∈[1,n]
并把
s[k]
上的字符替换成
#
。
对每一个
k∈[1,n]
,求修改后的字符串中本质不同的子串个数。
思路
设
i
位置的答案为
考虑每一种本质不同子串对答案的影响。
某一子串的出现位置为
(lj,rj)
,如果
∃j
,
i∉[lj,rj]
,那么就会对
g[i]
贡献1的答案。
我们发现这样太蛋疼了= =
考虑补集转换,对于某一个位置
i
,它变成了一个没有出现过的字符,我们先把所有可能增加的答案,即为跨过这个点的所有子串都加进去,再减去不合法的。
这样我们继续考虑本质不同子串对答案的影响,如果对于
我们发现这个东西就非常好统计了!
后缀三兄弟皆可搞!
在这里我选用了后缀自动机
我们需要的有四个信息:
1.
minx
,right集合中最小的位置。
2.
maxx
,right集合中最大的位置。
3.
maxlen
,所能接受的最长子串。
4.
minlen=pre→maxlen+1
,所能接受的最短子串。
可以通过简单的拓扑求出。
子串的贡献区间为
[maxx−len+1,minx]
后缀自动机一个节点表示多个right集合相同的不同长度的子串。
那么一个节点对
[maxx−len+1,maxx−minlen]
贡献一个首项为-1,公比为-1的等差数列;
对
[maxx−minlen+1,minx]
贡献
maxlen−minlen+1
可以用树状数组实现之,时间复杂度
O(nlogn)
。
二阶差分可以得到更优的时间复杂度。具体做法是先对
g
差分得到
时间复杂度
涨姿势……
代码
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define INF 0x3f3f3f3f
int n;
long long total;
long long ans[300010];
char st[600010];
namespace SAM
{
struct Node{
Node *pre,*next[26];
int val,minx,maxx;
Node(){};
Node(int);
}*null;
Node::Node(int _val)
{
val=_val;
for (int i=0;i<26;i++)
next[i]=null;
minx=INF;
maxx=0;
pre=null;
}
Node pool[600010];
Node *Root,*current;
int poolt,now;
int sum[600010],order[600010];
Node *NewNode(int _val)
{
pool[++poolt]=Node(_val);
return &pool[poolt];
}
void Init()
{
null=NewNode(0);
*null=Node(0);
Root=NewNode(0);
current=Root;
}
void extend(char ch)
{
int x=ch-'a';
Node *p=current;
Node *np=NewNode(p->val+1);
while (p!=null&&p->next[x]==null)
p->next[x]=np,p=p->pre;
if (p==null)
np->pre=Root;
else
{
Node *q=p->next[x];
if (q->val==p->val+1)
np->pre=q;
else
{
Node *nq=NewNode(p->val+1);
memcpy(nq->next,q->next,sizeof(q->next));
nq->pre=q->pre;
q->pre=nq;
np->pre=nq;
while (p!=null&&p->next[x]==q)
p->next[x]=nq,p=p->pre;
}
}
total+=np->val-np->pre->val;
np->maxx=np->val;
np->minx=np->val;
current=np;
}
void solve()
{
for (int i=2;i<=poolt;i++)
sum[pool[i].val]++;
for (int i=1;i<=n;i++)
sum[i]+=sum[i-1];
for (int i=2;i<=poolt;i++)
order[sum[pool[i].val]--]=i;
for (int i=poolt-1;i>=1;i--)
{
pool[order[i]].pre->minx=min(pool[order[i]].pre->minx,pool[order[i]].minx);
pool[order[i]].pre->maxx=max(pool[order[i]].pre->maxx,pool[order[i]].maxx);
}
for (int i=poolt-1;i>=1;i--)
if (pool[order[i]].maxx-pool[order[i]].val+1<=pool[order[i]].minx)
{
int l=pool[order[i]].maxx-pool[order[i]].val+1;
int r=min(pool[order[i]].maxx-(pool[order[i]].pre->val+1)+1,pool[order[i]].minx);
int pp=r-l+1;
if (l<=r)
{
ans[l]++;
ans[r+1]-=pp+1;
ans[r+2]+=pp;
}
l=r+1,r=pool[order[i]].minx;
if (l<=r)
{
ans[l]+=pp;
ans[l+1]-=pp;
ans[r+1]-=pp;
ans[r+2]+=pp;
}
}
for (int i=1;i<=n;i++)
ans[i]+=ans[i-1];
for (int i=1;i<=n;i++)
ans[i]+=ans[i-1];
}
}
void init()
{
scanf("%d",&n);
scanf("%s",st+1);
SAM::Init();
for (int i=1;i<=n;i++)
SAM::extend(st[i]);
}
void work()
{
SAM::solve();
for (int i=1;i<=n;i++)
printf("%lld ",1LL*i*(n-i+1)+total-ans[i]);
}
int main()
{
init();
work();
return 0;
}