[論序列自動機的奇怪姿勢]

序列自動機構建是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;
}


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