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