[论序列自动机的奇怪姿势]

序列自动机构建是O(n|α|)的喔!

其中|α|是字符集的大小


然而有一种优雅的构建方式

可以实现O(nlogα)的创建和查询喔QAQ

妈妈再也不用担心我只会a~z了


给定一个字符串,求字典序第k小的子序列
位置不同本质相同的子序列算一个。

建成序列自动机,然后利用可持久化线段树从后往前统计每个节点的后继数量
查找的时候在可持久化线段树上二分即可



Orz PoPoQQQ大爷

#include 
#include 
#include 
#include 
#define M 100100
using namespace std;

int n, k;
int a[M];

struct Segtree{
	Segtree *ls, *rs;
	int sum, pos;
	Segtree(Segtree *_, Segtree *__, int ___, int ____):
		ls(_), rs(__), sum(___), pos(____){}
	friend Segtree* Insert(Segtree *p, int l, int r, int x, int val, int pos){
		int mid = l + r >> 1;
		if(l == r)return new Segtree(0x0, 0x0, val, pos);
		if(x <= mid){
            Segtree *temp = Insert(p->ls, l, mid, x, val, pos);
            return new Segtree(temp, p->rs, min(temp->sum + p->rs->sum, k+1), 0);
		}
		else{
            Segtree *temp = Insert(p->rs, mid+1, r, x, val, pos);
            return new Segtree(p->ls, temp, min(temp->sum + p->ls->sum, k+1), 0);
		}
	}
	friend int Find(Segtree *p, int l, int r){
		int mid = l + r >> 1;
		if(l == r)return p->pos;
		if(k <= p->ls->sum)
		    return Find(p->ls, l, mid);
		else{
			k -= p->ls->sum;
			return Find(p->rs, mid+1, r);
		}
	}
};

Segtree *tree[M];

int st[M], top;

int main(){
    freopen("C.in", "r", stdin);
	freopen("C.out", "w", stdout);
	cin >> n >> k; ++ k;
	int mx = 0;
	for(int i = 1; i <= n; i ++)
		scanf("%d", &a[i]), mx = max(mx, a[i]);
		
	tree[n+1] = new Segtree(0x0, 0x0, 0, 0);
	tree[n+1]->ls = tree[n+1]->rs = tree[n+1];
	
	for(int i = n+1; i >= 0; i --)
	    tree[i-1] = Insert(tree[i], 0, mx, a[i], i == n+1 ? 1 : tree[i]->sum, i);
	    
	if(k > tree[0]->sum)
	    return puts("-1"), 0;
	    
	int now = 0;
	while(true){
		now = Find(tree[now], 0, mx);
		if(now == n+1)break;
		st[++ top] = a[now];
	}
	
	printf("%d\n", top);
	for(int i = 1; i <= top; i ++)
	    printf("%d ", st[i]);
	return 0;
}

呢。这里顺便更前两道题的题解嘛

Problem B:

对于要把01串A和B变得相同,每次只能翻转一个区间求代价最小

我们可以把它转化成差分后两点之间的修改

然后两点之间连边

可以证明一定不会存在环->森林!

然后就将森林拆成最短路的链(跑Floyd最短路啦)

可以证明差分之后k是个偶数,一定可以配对成k/2个链

然后跑一般图的最小权匹配诶


Problem A:

[NOIP2008] 传纸条

相信大家都做过嘛

大家还是n^3dp吗??

现在求n,m<=10^15怎么捉?

我们有公式:ans = C[n+m-2][m-1] * C[n+m-2][n-1] - C[n+m-2][m-2] * C[n+m-2][n-2]

具体打表嘛观察观察

模数比较坑,2^32次方

对于模数和阶乘如何取逆元??

容易发现2对于2^32不能用费马小定理直接求逆元

那么怎么办??

把他们变成互质的=>

a * 2^b

然后指数相减,奇数求逆元就好了

暴力是这样

然而更大的数据要用到分治

可以做到P ^ {k/2}


好吧其实我并不会

比如用(2 ^ 16)分块

1 3 5 7 ........ 65535 65537 65539 65541 ...... ......

65537 = 2^16+1

65539 = 2^16+3

.....

2^32 - 1

对于每(2 ^ 16)分个块

关于2^16的多项式

0次项为1 * 3 * 5 * 7 .....

1次项为(2^16) * (k * (k-1) / 2) * i ^ {k-1}

其中k是i在所有块中出现的次数

然后2次项被模掉辣


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define B (1<<16)
using namespace std;
typedef pair<unsigned int,long long> abcd;
long long n,m;
unsigned int Quick_Power(unsigned int x,long long y)
{
	unsigned int re=1;
	while(y)
	{
		if(y&1) re*=x;
		x*=x; y>>=1;
	}
	return re;
}
abcd operator * (const abcd &x,const abcd &y)
{
	return abcd(x.first*y.first,x.second+y.second);
}
abcd operator / (const abcd &x,const abcd &y)
{
	return abcd(x.first*Quick_Power(y.first,(1ll<<31)-1),x.second-y.second);
}
abcd Factorial(long long n)
{
	if(!n) return abcd(1u,0);
	unsigned int i,m=n,fac=1;
	for(i=1;i<B && i<=m;i+=2)
	{
		unsigned int k=m/B+(m%B>=i);
		fac*=Quick_Power(i,k)+(B>>1)*k*(k-1)*Quick_Power(i,k-1);
	}
	return abcd(fac,n>>1)*Factorial(n>>1);
}
unsigned int C(long long n,long long m)
{
	abcd re=Factorial(n)/Factorial(m)/Factorial(n-m);
	return re.first*Quick_Power(2,re.second);
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	cin>>n>>m;
	if(n==1||m==1)
		cout<<2<<endl;
	else
		cout<<2u*(C(n+m-2,n-1)*C(n+m-2,n-1)-C(n+m-2,n)*C(n+m-2,m))<<endl;
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章