const int N = 1e5+5;
int lca[N][20];
int dep[N];
void dfs(int now,int f,int depth){///初始时dfs(根节点,0,1);
lca[now][0] = f;
dep[now] = depth;
for (int i=head[now];i!=0;i=edge[i].last){//链式前向星
int v = edge[i].to;
if (v==f) continue;
dfs(v,now,depth+1);
}
}
void getst(int n){
for (int j=1;(1<=0;i--){
if (lca[x][i]!=lca[y][i]) x = lca[x][i],y = lca[y][i];
}
return lca[x][0];
}
///求所有循环同构中字典序最小或者最大
int getmin(int len)
{
int i=0,j=1,k=0;
while (i0) j += k+1;
else i += k+1;
if (i==j) j++;
k = 0;
}
}
return i
const int maxn=1000010;
char str[maxn];//原字符串
char tmp[maxn<<1];//转换后的字符串
int Len[maxn<<1];
//转换原始串
int INIT(char *st)
{
int i,len=strlen(st);
tmp[0]='@';///开头加一个不等于#也不等于字符串的字符,这样就不用判断左边越界了,那么右边万一比到n+1怎么办呢?有\0吗?不,\0在n处,解决办法看16行
for(i=1;i<=2*len;i+=2)
{
tmp[i]='#';
tmp[i+1]=st[i/2];
}
tmp[2*len+1]='#';
tmp[2*len+2]='$';///结尾搞一个不是@也不是#的字符
tmp[2*len+3]=0;
return 2*len+1;//返回转换字符串的长度
}
//Manacher算法计算过程
int MANACHER(char *st,int len)
{
int mx=0,ans=0,po=0;//mx即为当前计算回文串最右边字符的最大值
for(int i=1;i<=len;i++)
{
if(mx>i)
Len[i]=min(mx-i,Len[2*po-i]);//在Len[j]和mx-i中取个小
else
Len[i]=1;//如果i>=mx,要从头开始匹配
while(st[i-Len[i]]==st[i+Len[i]])
Len[i]++;
if(Len[i]+i>mx)//若新计算的回文串右端点位置大于mx,要更新po和mx的值
{
mx=Len[i]+i;
po=i;
}
ans=max(ans,Len[i]);
}
return ans-1;//返回Len[i]中的最大值-1即为原串的最长回文子串额长度
}
Len[st+ed]-1>=ed-st+1判断区间是否回文
const int maxn=100010; //字符串长度最大值
int nt[maxn],ex[maxn]; //ex数组即为extend数组
///预处理计算next数组
void GETNEXT(char *str)
{
int i=0,j,po,len=strlen(str);
nt[0]=len;///用自己作为后缀与自己匹配
while(i+1
#include
const int N = 1e6+5;
using namespace std;
int nt[N];
char s[N];
char ss[N];
int KMP() ///求大串中有几个子串
{
int len=strlen(s);
int t=strlen(ss);
nt[0]=-1;
int cnt=0;
///构造next数组
for (int i=0,j=-1; i>s){
cin>>ss;
cout<
int trie[maxnode][sigma_size];///maxnode表示节点数,maxnode=单词数*每个单词最大长度(最坏情况每个单词组合都不一样)
///sigma_size 表示一个节点最多延伸出多少各节点,也就是字符种类
/// trie[i][j] 的值表示 i节点指向j这个数字对应字母 的节点的位置
bool val[maxnode]; ///为真表示当前节点是一个单词的结束
int sz;
int idx(char ch) { return ch-'a';}//将字母转换成对应数字,如果还有大写,标点的话要更麻烦些
void init(int x){
val[x] = false;
memset(trie[x],0,sizeof trie[x]);
}
void insert(char *s){///插入
int u = 0;
for (int i=0;s[i];i++){
int v = idx(s[i]);
if (!trie[u][v]){
init(sz);///初始化多组输入时上一次残留的数据
trie[u][v] = sz++;
}
u = trie[u][v];
}
val[u] = true;
}
bool query(char *s){
int u = 0;
for (int i=0;s[i];i++){
int v = idx(s[i]);
if (!trie[u][v]) return false; ///这个节点还没有开辟
u = trie[u][v];
}
return true;
}
#include
#include
#define ll long long
using namespace std;
const int N = 4000*1001 + 5;
struct node
{
int son;
int right;
int sum;
char ch;
}trie[N];
int id;
ll ans;
void init()
{
ans = 0;
id = 1;
trie[0].right = 0;
trie[0].son = 0;
trie[0].sum = 0;
}
void insert(char *s)
{
int u = 0,j;
int len = strlen(s);
for (int i=0;i<=len;i++){
bool flag = false;
for (j=trie[u].son;j!=0;j=trie[j].right){
if (s[i]==trie[j].ch){
flag = true;
break;
}
}
if (!flag){
j = id++;
trie[j].right = trie[u].son;
trie[u].son = j;
trie[j].ch = s[i];
trie[j].son = 0;
trie[j].sum = 0;
}
ans += (trie[u].sum+trie[j].sum);
if (i==len) trie[j].sum++;
trie[u].sum++;
u = j;
}
}
int main()
{
int n;
char in[1010];
for (int kca=1;scanf("%d",&n),n;kca++){
init();
while (n--) scanf("%s",in),insert(in);
printf("Case %d: %lld\n",kca,ans);
}
}
#include
#include
#include
#define debug(x) printf("----Line%s----\n",#x)
using namespace std;
const int N = 1e5+5;
int wa[N<<1],wb[N<<1],sa[N<<1],rnk[N<<1],cnt[N<<1],height[N<<1];
char s[N<<1];
void build(int n,int m)
{
int *x=wa,*y=wb,p,i,j;
for (i=0;i=0;i--) sa[--cnt[x[i]]] = i;
for (j=1,p=1;p=j) y[p++] = sa[i]-j;
for (i=0;i=0;i--) sa[--cnt[x[y[i]]]] = y[i];
swap(x,y);
x[sa[0]] = 0;
p = 1;
for (i=1;ilen) || (sa[i]>len && sa[i-1]
#include
#include
#include
#include
#define debug(x) printf("----line %s----\n",#x)
using namespace std;
const int N = 2e5 + 5;
int sa[N],wa[N],wb[N],rnk[N],cnt[N],height[N];
char s[N];
int st[N][20];
void buildsa(int n,int m)///m是初始字符种类,可以偏大
{
int *x = wa,*y = wb,p,i,j;
for (int i=0;i=0;i--) sa[--cnt[x[i]]] = i;
for (j=1,p=1;p=j) y[p++] = sa[i]-j;///得出根据第二关键字排名的
for (int i=0;i=0;i--) sa[--cnt[x[y[i]]]] = y[i];
swap(x,y);
x[sa[0]] = 0;
p = 1;
for (i=1;ir) swap(l,r);
if (l==r){
ans = min(b-a+1,d-c+1);
}
else {
ans = min(min(b-a+1,d-c+1),query(l+1,r));
}
printf("%d\n",ans);
}
return 0;
}
const int power = 4; ///每次运算的位数为10的power次方,在这里定义为了方便程序实现
const int base = 10000; ///10的power次方。
const int MAXL = 200; ///MAXL*POWER = 表示的数的范围
char a[MAXL], b[MAXL];
struct num
{
int a[MAXL];///如果POWER>4,不修改为修改为LL乘法会爆
num() { memset(a, 0, sizeof(a)); } ///初始化
num(char *s) ///将一个字符串初始化为高精度数
{
memset(a, 0, sizeof(a));
int len = strlen(s);
a[0] = (len+power-1) / power; ///数的长度
for (int i=0, t=0, w; i < len ;w *= 10, ++i)
{
if (i % power == 0) { w = 1, ++t; }
a[t] += w * (s[i]-'0');
}
}
void add(int k) { if (k || a[0]) a[ ++a[0] ] = k; } ///在末尾添加一个数,除法的时候要用到,不是当前数+k!!
void re() { reverse(a+1, a+a[0]+1); } ///把数反过来,除法的时候要用到
void print() ///打印此高精度数
{
printf("%d", a[ a[0] ]);///先打印最高位,为了压位 或者 该高精度数为0 考虑
for (int i = a[0]-1;i > 0;--i)
printf("%0*d", power, a[i]);///这里"%0*d", power的意思是,必须输出power位,不够则前面用0补足
printf("\n");
}
} p,q,ans;
bool operator < (const num &p, const num &q) ///判断小于关系,除法的时候有用
{
if (p.a[0] < q.a[0]) return true;
if (p.a[0] > q.a[0]) return false;
for (int i = p.a[0];i > 0;--i)
{
if (p.a[i] != q.a[i]) return p.a[i] < q.a[i];
}
return false;
}
num operator + (const num &p, const num &q) ///加法,不用多说了吧,模拟一遍,很容易懂
{
num c;
c.a[0] = max(p.a[0], q.a[0]);
for (int i = 1;i <= c.a[0];++i)
{
c.a[i] += p.a[i] + q.a[i];
c.a[i+1] += c.a[i] / base;
c.a[i] %= base;
}
if (c.a[ c.a[0]+1 ]) ++c.a[0];
return c;
}
num operator - (const num &p, const num &q) ///减法,也不用多说,模拟一遍,很容易懂
{
num c = p;
for (int i = 1;i <= c.a[0];++i)
{
c.a[i] -= q.a[i];
if (c.a[i] < 0) { c.a[i] += base; --c.a[i+1]; }
}
while (c.a[0] > 0 && !c.a[ c.a[0] ]) --c.a[0];
///我的习惯是如果该数为0,那么他的长度也是0,方便比较大小和在末尾添加数时的判断。
return c;
}
num operator * (const num &p, const num &q)
///乘法,还是模拟一遍。。其实高精度就是模拟人工四则运算!
{
num c;
c.a[0] = p.a[0]+q.a[0]-1;
for (int i = 1;i <= p.a[0];++i)
for (int j = 1;j <= q.a[0];++j)
{
c.a[i+j-1] += p.a[i]*q.a[j];
c.a[i+j] += c.a[i+j-1] / base;
c.a[i+j-1] %= base;
}
if (c.a[ c.a[0]+1 ]) ++c.a[0];
return c;
}
num operator / (const num &p, const num &q) ///除法,这里我稍微讲解一下
{
num x, y;
for (int i = p.a[0];i >= 1;--i) ///从最高位开始取数
{
y.add(p.a[i]); ///把数添到末尾(最低位),这时候是高位在前,低位在后
y.re(); ///把数反过来,变为统一的存储方式:低位在前,高位在后
while ( !(y < q) ) ///大于等于除数的时候,如果小于的话,其实答案上的该位就是初始的“0”
y = y - q, ++x.a[i]; ///看能减几个除数,减几次,答案上该位就加几次。
y.re(); ///将数反过来,为下一次添数做准备
}
x.a[0] = p.a[0];
while (x.a[0] > 0 && !x.a[x.a[0]]) --x.a[0];
return x;
}
p = k*i+r
k*i+r === 0 mod p,然后两边同乘以r^-1*i^-1
k*r^-1 + i^-1 === 0 mod p
inv[i] = -k * (1/r) mod p
inv[i] = (p-[p/i])*inv[p%i]%p
inv[1] = 1;
for (ll i=2;i<=n;i++)
inv[i] = (mod-mod/i)*inv[mod%i]%mod;
第28行有修改,之前可能爆int了
先理清几个东西:
因为ax + by = c有解--->c是gcd(a,b)的整数倍--->求解出ax+by=gcd(a,b)的x,y都乘以c/gcd(a,b)即可
所以ax+by=1有解--->互质--->gcd(a,b) = 1
求解一次线性同余方程组
x === a1(mod m1)
x === a2(mod m2)
......
x = k1*m1+a1 = k2*m2+a2
先求解 m1*k1 - m2*k2 = a2-a1
求解出特解k1,得到 x = k1*m1+a1为满足第一第二个式子的特解,此时第一第二个式子通解为XX = x + LCM(m1,m2)*k
即类似于x = ki*mi+ai的形式,这个通解的式子继续和下一项重复上述步奏
void exgcd(ll a,ll b,ll &x,ll &y)
{
if (b==0){x = 1;y = 0;}
else{
exgcd(b,a%b,y,x);
y = y - a/b*x;
}
}
ll gcd(ll a,ll b)
{
return b==0? a:gcd(b,a%b);
}
int main()
{
ll a1,a2,r1,r2,x,y;
int n;
while (~scanf("%d",&n)){
scanf("%lld %lld",&a1,&r1);///x === a1 mod r1
bool f = 1;
for (int i=2;i<=n;i++){
scanf("%lld %lld",&a2,&r2);///a1*x + r1 = a2*y + r2
ll r = gcd(a1,-a2); ///a1*x - a2*y = r2-r1
if ((r2-r1)%r!=0){f = 0;continue;}///无解
exgcd(a1,-a2,x,y);///算出来的是a1*x-a2*y = gcd(a1,a2)的解
ll p = a2/gcd(a1,a2);
x *= (r2-r1)/r; x = (x%p+p)%p;///求得实际x
///如果x>a2,x = xx + a2
///a1*(xx+a2) + a2*y =c 有解,那么a1*xx + a2*(y+a1)=c有解
///对于ax+by=c,如果有整数解x,y && x大于b,一定存在x小于b的整数解
/// X = (a1*x+r1) + LCM(a1,a2)*k
/// X = ri + ai*x
r1 = a1*x+r1;
a1 = a1/gcd(a1,a2)*a2;
r1 %= a1;///这一步没毛病
}
if (!f) puts("-1");
else printf("%lld\n",r1);
}
return 0;
}
来自:https://blog.csdn.net/u013368721/article/details/42100363
#include
using namespace std;
const int maxn = 3e5+5;
const int ALP = 26;
struct PAM{ // 每个节点代表一个回文串
int next[maxn][ALP]; // next指针,参照Trie树
int fail[maxn]; // fail失配后缀链接
int cnt[maxn]; // 此回文串出现个数
int num[maxn];
int len[maxn]; // 回文串长度
int s[maxn]; // 存放添加的字符
int last; //指向上一个字符所在的节点,方便下一次add
int n; // 已添加字符个数
int p; // 节点个数
int l[maxn],r[maxn];//i节点代表的回文串第一次出现最左左右字符是第几个(不是下标)
int newnode(int w){//新建一个节点,长度初始化为这个节点代表的回文串长度
for(int i=0;i=0;i--)
cnt[fail[i]] += cnt[i];
}
}pam;
const int maxn = 1e5+5;
const int ALP = 26;
struct PAM{ // 每个节点代表一个回文串
int next[maxn][ALP]; // next指针,参照Trie树
int fail[maxn]; // fail失配后缀链接
int num[maxn];
int len[maxn]; // 回文串长度
int s[maxn<<1]; // 存放添加的字符
int l_last,r_last; //指向上一个字符所在的节点,方便下一次add
int l,r; //r-l+1表示现在添加的字符数量
int p; // 节点个数
ll cnt;//记录当前回文串总数
int newnode(int w){//新建一个节点,长度初始化为这个节点代表的回文串长度
for(int i=0;i
找不到原文放不了链接了。
#include
using namespace std;
namespace FastI{
const int SIZE = 1 << 16;
char buf[SIZE], str[64];
int l = SIZE, r = SIZE;
int read(char *s) {
while (r) {
for (; l < r && buf[l] <= ' '; l++);
if (l < r) break;
l = 0, r = int(fread(buf, 1, SIZE, stdin));
}
int cur = 0;
while (r) {
for (; l < r && buf[l] > ' '; l++) s[cur++] = buf[l];
if (l < r) break;
l = 0, r = int(fread(buf, 1, SIZE, stdin));
}
s[cur] = '\0';
return cur;
}
template
bool read(type &x, int len = 0, int cur = 0, bool flag = false) {
if (!(len = read(str))) return false;
if (str[cur] == '-') flag = true, cur++;
for (x = 0; cur < len; cur++) x = x * 10 + str[cur] - '0';
if (flag) x = -x;
return true;
}
template
type read(int len = 0, int cur = 0, bool flag = false, type x = 0) {
if (!(len = read(str))) return false;
if (str[cur] == '-') flag = true, cur++;
for (x = 0; cur < len; cur++) x = x * 10 + str[cur] - '0';
return flag ? -x : x;
}
} using FastI::read;
int main() {
//freopen("C:/Users/DELL/Desktop/input.txt", "r", stdin);
//freopen("C:/Users/DELL/Desktop/output.txt", "w", stdout);
int a[1000];
while (read(a[1])){printf("%d",a[1]+1);}
return 0;
}
const int maxnode = 1e6+5;//模式串数量*长度
const int ALP = 26;//字符种类数
struct AC_am
{
queueque;
int sz;
int trie[maxnode][ALP];
int fail[maxnode];
int last[maxnode];
int val[maxnode];//储存当前节点信息,如是否为单词节点等等
int newnode(int x){
memset(trie[x],0,sizeof trie[x]);
val[x] = 0;
return sz++;
}
void init(){
newnode(sz = 0);
}
int idx(char ch){//实际字符串转化为字典树对应节点,根据题目做出具体改变
return ch-'a';
}
void insert(char *s){
int u = 0;
for (int i=0;s[i];i++){
int c = idx(s[i]);
if (!trie[u][c]){
trie[u][c] = newnode(sz);
}
u = trie[u][c];
}
val[u]++;
}
void build(){
fail[0] = 0;
for (int c=0;c
矩阵构造方法:写出两层递推式子,递推式有几项就构造几×几
const int matsize = 30;///矩阵最大大小
ll temp[matsize][matsize],ans[matsize][matsize];
int ms;///实际矩阵大小,每次矩阵快速幂之前根据矩阵大小修改
void multy(ll a[][matsize],ll b[][matsize])
{
memset(temp,0,sizeof temp);
for (int i=0;i>=1;
multy(a,a);
}
for (int i=0;i
作用:求任意两点最短路
时间:O(V^3)
#define for0(i,a,b) for (int i=a;i
dp[i][j]表示两个点之间可以用1~k-1作为中途点时的最短路径,输入取最小值防止有权值不同的重边
判断有无负环检查是否有dp[i][i]<0,有负环的话就没有最短路径了
作用:求单源最短路
时间:O(E*logE)
const int N = 1e5+5;///点的数量
const int M = 5e5+5;///边的数量
struct Edge//储存边
{
int to;
int last;
int w;
}edge[M];
int head[N],id;
void add(int u,int v,int w)//建从u->v,权值为w的边
{
edge[id].to = v;
edge[id].w = w;
edge[id].last = head[u];
head[u] = id++;
}
void init()//建边前的初始化
{
memset(head,0,sizeof head);
id = 1;
}
struct node//储存点
{
int now;
int w;//到达now节点的这条边的权值
bool operator < (const node& a)const{//***比较方式要和自己想的反过来***
return w>a.w;
}
};
bool vis[N];//是否求出最短路径
void dijkstra()
{
memset(vis,0,sizeof vis);
priority_queueque;
int root = 1;//单元最短路径的源点
que.push({root,0});
while (!que.empty()){
node now = que.top();que.pop();
if (vis[now.now]) continue;
vis[now.now] = true;
/*
当前点now记录了这个点到源点的最短距离,问题可以在这儿处理
*/
for (int i=head[now.now];i!=0;i=edge[i].last){
que.push({edge[i].to,edge[i].w+now.w});//***权值记得要加now.w***
}
}
}
不能处理有负权的边的问题,否则当前连到树上的点不一定是最短路径。
跑完dijkstra后相当于建了一棵包含所有节点的树,树上所有点到源点的距离最近。
作用:求带负边的单源最短路/判负环
时间:O(V*E)
#define INF 0x3f3f3f3f
const int N = 1e4+5;
const int M = 5e4+5;
struct Edge
{
int to,last,w;
}edge[M];
int id,head[N];
void add(int u,int v,int w)
{
edge[id].to = v;
edge[id].w = w;
edge[id].last = head[u];
head[u] = id++;
}
void init()
{
id = 1;
memset(head,0,sizeof head);
}
int dis[N];
void Bellman_ford(int V)//V表示节点数
{
int root = 1;//若只是判断负环,随便取一个源点即可
memset(dis,INF,sizeof dis);
dis[root] = 1;
for (int k=1;kdis[u]+edge[i].w){
dis[v] = dis[u] + edge[i].w;
/*
若要判断负环,将最外层循环修改为k<=V,并判断k==V时是否还有被修改的点
有则证明有负环,因为有负环会一直更新
*/
}
}
/*
求得的dis[i]即为i到root的最短路径
*/
}
第k次更新dis[i]相当于在更新:i沿着边反向走k步途中能到达的所有点形成的这个图中,i距离源点的最短路径
上一次我们求出了k-1步下每个点的单元最短路径,因此我们用所有能直接一步到达i的节点去更新即可。
当然有些点其实在第k次被更新好了,然后又去更新了别人,这样对那个被更新的点最终单源最短路的结果没有影响,就是可能提前获取了答案。
显然更新完V-1步,所有点的单源最短路都正确了,因为V-1步已经包含整张图了。还能更新说明有负环。
作用:求单源最短路/判负环
时间:O(V*E) (一般情况远低于)
const int N = 1e4+5;
const int M = 5e4+5;
struct Edge
{
int to,last,w;
}edge[M];
int id,head[N];
void add(int u,int v,int w)
{
edge[id].to = v;
edge[id].w = w;
edge[id].last = head[u];
head[u] = id++;
}
void init()
{
id = 1;
memset(head,0,sizeof head);
}
int val[N];//记录该节点被更新多少次
int dis[N];//记录单源最短路
void SPFA(int V)//V表示节点数
{
memset(dis,INF,sizeof dis);
memset(val,0,sizeof val);
int root = 1;//若只用于判负环,源点可以随便取
bool flag = false;//是否有负环,初始化为无
dis[root] = 0;
/*
下面注释掉的四行是SPFA的优化,节点数多的时候一般来说是会优化...
就是先让dis[]小的去松弛,这样可以让进队列的数更少
*/
//dequeque;
//que.pb(root);
queueque;
que.push(root);
while (!que.empty()){
int now = que.front();que.pop_front();
int v;
for (int i=head[now];i!=0;i=edge[i].last){
v = edge[i].to;
if (dis[v]>dis[now]+edge[i].w){
dis[v] = dis[now] + edge[i].w;
//que.pb(v);
//if (dis[que.front()]
优化原理是未被松弛成功的点一定不会对下一轮松弛产生影响,因此用队列保留被松弛过的点去更新即可。
const int MAXN = 250000+5;
const int ALP = 26;
struct SAM
{
int trie[MAXN<<1][ALP];
int len[MAXN<<1];
int fa[MAXN<<1];
int sz,las;
void init(){
newnode(las=sz=0);
fa[0] = -1;
}
int newnode(int x){
memset(trie[x],0,sizeof trie[x]);
len[x] = 0;
return sz++;
}
int idx(char ch){
return ch-'a';
}
void add(char ch){
int c = idx(ch);
int p = las;
int np = newnode(sz);
las = np;
len[np] = len[p]+1;
for(;~p && !trie[p][c];p=fa[p]) trie[p][c] = np;/**这里循环停下来其实很关键的,停下来的话证明上一个后缀+C的对应的串存在,那个玩意儿就是现在的最长后缀了,不会更长了,否则之前就找到末尾+c有指向的点了*/
if (p==-1) fa[np] = 0;
else {
int q = trie[p][c];
if (len[q]==len[p]+1) fa[np] = q;
else {
int nq = newnode(sz);
fa[nq] = fa[q];
for0(i,0,ALP) trie[nq][i] = trie[q][i];
len[nq] = len[p]+1;
fa[np] = fa[q] = nq;
for (;~p && trie[p][c]==q;p=fa[p]) trie[p][c] = nq;/**之前所有连到q的现在全部连到nq,因为要连到最短后缀,只需证明+C小于等于nq即可,显然的*/
}
}
}
int find(char *s){
int u=0,maxlen=0,nowlen=0;
for (int i=0;s[i];i++){
int c = idx(s[i]);
while (~u && !trie[u][c]){
u = fa[u];
if (~u) nowlen = len[u];
}
if (u==-1) {nowlen = u = 0;continue;}
u = trie[u][c];
nowlen++;
maxlen = max(maxlen,nowlen);
}
return maxlen;
}
}sam;
来自:https://blog.csdn.net/lvzelong2014/article/details/79006541
#include
#include
#include
#include
#include
#include
插入与找第k小(提醒一下引用不要漏掉)
#include
using namespace std;
struct Treap
{
Treap* ch[2];
int val;
int pri;
int cnt;
Treap(int x){ch[0] = ch[1] = NULL; pri = rand(); val = x;cnt = 1;}
void maintain(){
cnt = 1;
if (ch[0]!=NULL) cnt += ch[0]->cnt;
if (ch[1]!=NULL) cnt += ch[1]->cnt;
}
};
void rotate(Treap* &rt,int d)
{
Treap* k = rt->ch[d^1];
rt->ch[d^1] = k->ch[d];
k->ch[d] = rt;
rt->maintain();
k->maintain();
rt = k;
}
void insert(Treap* &rt,int x)
{
if (rt==NULL){rt = new Treap(x);}
else {
int d = (x < rt->val)? 0:1 ;
insert(rt->ch[d],x);
if (rt->ch[d]->pri > rt->pri) rotate(rt,d^1);
}
rt->maintain();
}
int find(Treap* rt,int k)
{
if (rt->ch[0]==NULL) ccnt = 1;
else ccnt = 1 + rt->ch[0]->cnt;
if (k==ccnt) return rt->val;
else if (kch[0],k);
else return find(rt->ch[1],k-ccnt);
}
int main()
{
Treap* root = NULL;
insert(root,1);
insert(root,3);
insert(root,5);
printf("%d\n",find(root,2));
}
const int N = 1e5+5;
int a[N],n;//用于构建的数组
int sta[N],root;//栈,根节点
int ch[N][2],l[N],r[N];//树,比自己大的最左最右下标
void build(){
int tot = -1;
for1(i,1,n){
ch[i][0] = ch[i][1] = 0;//初始化
int nowtot = tot;//用于检验栈内是否弹出了元素,否则sta[tot+1]不知道连到什么鬼玩意
while (tot>=0 && a[sta[tot]]>a[i]) tot--;
if (tot>=0) ch[sta[tot]][1] = i;
if (nowtot>tot) ch[i][0] = sta[tot+1];
sta[++tot] = i;
}
root = sta[0];
}
void dfs(int now){//获取l,r
if (ch[now][0]) dfs(ch[now][0]);
l[now] = ch[now][0] ? l[ch[now][0]]:now;
if (ch[now][1]) dfs(ch[now][1]);
r[now] = ch[now][1] ? r[ch[now][1]]:now;
}
#include
using namespace std;
#define ll long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i pri[y]){
ch[x][1] = merge(ch[x][1],y);
maintain(x);
return x;
}
else {
ch[y][0] = merge(x,ch[y][0]);
maintain(y);
return y;
}
}
void split(int rt,int v,int& x,int& y){
if (!rt) x = y = 0;
else {
if (val[rt] <= v){
x = rt;
split(ch[rt][1],v,ch[rt][1],y);
maintain(x);
}
else {
y = rt;
split(ch[rt][0],v,x,ch[rt][0]);
maintain(y);
}
}
}
void insert(int v){
int x,y;
split(root,v,x,y);
root = merge(merge(x,newnode(v)) , y);
}
void remove(int v){
int x,y,z;
split(root,v-1,x,y);
split(y,v,y,z);
y = merge(ch[y][0],ch[y][1]);
root = merge(merge(x,y),z);
}
int rank(int v){
int x,y;
split(root,v-1,x,y);
int ans = cnt[x]+1;
root = merge(x,y);
return ans;
}
int KTH(int k){return val[kth(root,k)];}
int kth(int rt,int k){
if (k<=cnt[ch[rt][0]]) return kth(ch[rt][0],k);
k -= cnt[ch[rt][0]] + 1;
if (k<=0) return rt;
else return kth(ch[rt][1],k);
}
int pre(int v){
int x,y;
split(root,v-1,x,y);
int ans = val[kth(x,cnt[x])];
root = merge(x,y);
return ans;
}
int suc(int v){
int x,y;
split(root,v,x,y);
int ans = val[kth(y,1)];
root = merge(x,y);
return ans;
}
}ftp;
int main()
{
ftp.init();
int m,op,x;
scanf("%d",&m);
while (m--){
scanf("%d %d",&op,&x);
if (op==1) ftp.insert(x);
if (op==2) ftp.remove(x);
if (op==3) printf("%d\n",ftp.rank(x));
if (op==4) printf("%d\n",ftp.KTH(x));
if (op==5) printf("%d\n",ftp.pre(x));
if (op==6) printf("%d\n",ftp.suc(x));
}
return 0;
}
struct fhq_treap
{
int ch[maxn][2];
int pri[maxn];
int cnt[maxn];
int val[maxn];
int sum[maxn];
int l[maxn],r[maxn],mcs[maxn];
int rev[maxn],upd[maxn];
int sta[maxn];
int root;
int nextpos[maxn],st,ed;///储存下一个可以用的节点编号
void init(){
st = 1,ed = 1;
root = 0;
l[0] = r[0] = mcs[0] = -INF;
for (int i=1;i<=500005;i++) nextpos[i] = i;
}
int newnode(int v){
int sz = nextpos[st++]; if (st==500006) st = 1;
ch[sz][0] = ch[sz][1] = 0;
pri[sz] = rand();
cnt[sz] = 1;
val[sz] = sum[sz] = l[sz] = r[sz] = mcs[sz] = v;
rev[sz] = 0;
upd[sz] = 0;
return sz;
}
void use_for_reverse(int rt){
if (!rt) return ;
rev[rt] ^= 1;
swap(l[rt],r[rt]);
}
void use_for_update(int rt,int c){
if (!rt) return ;
upd[rt] = 1;
val[rt] = c;
sum[rt] = c*cnt[rt];
l[rt] = r[rt] = mcs[rt] = max(c,sum[rt]);
}
void update(int rt){
cnt[rt] = 1 + cnt[ch[rt][0]] + cnt[ch[rt][1]];
sum[rt] = val[rt] + sum[ch[rt][0]] + sum[ch[rt][1]];
l[rt] = max(max(l[ch[rt][0]],sum[ch[rt][0]]+val[rt]),sum[ch[rt][0]]+val[rt]+l[ch[rt][1]]);
r[rt] = max(max(r[ch[rt][1]],sum[ch[rt][1]]+val[rt]),sum[ch[rt][1]]+val[rt]+r[ch[rt][0]]);
mcs[rt] = max(max(mcs[ch[rt][0]],mcs[ch[rt][1]]),max(0,r[ch[rt][0]])+val[rt]+max(0,l[ch[rt][1]]));
}
void push_down(int rt){
if (!rt) return ;
if (rev[rt]){
use_for_reverse(ch[rt][0]);
use_for_reverse(ch[rt][1]);
rev[rt] = 0;
swap(ch[rt][0],ch[rt][1]);
}
if (upd[rt]){
use_for_update(ch[rt][0],val[rt]);
use_for_update(ch[rt][1],val[rt]);
upd[rt] = 0;
}
}
int merge(int x,int y){
if (!x || !y) return x+y;
push_down(x),push_down(y);
if (pri[x] < pri[y]){///维护大根堆
ch[y][0] = merge(x,ch[y][0]);
update(y);
return y;
}
else {
ch[x][1] = merge(ch[x][1],y);
update(x);
return x;
}
}
int ccnt;
void split(int rt,int k,int& x,int& y){
if (!rt) x = y = 0;
else {
push_down(rt);
ccnt = cnt[ch[rt][0]] + 1;
if (k>=ccnt){///***这边需要一个等号,思考方法是判断此时根+左儿子是否属于前k个
x = rt;
split(ch[rt][1],k-ccnt,ch[rt][1],y);
}
else {
y = rt;
split(ch[rt][0],k,x,ch[rt][0]);
}
update(rt);
}
}
int build(int n){
int x;
int tot = -1,st_tot;
for (int i=1;i<=n;i++){
read(x);
int now = newnode(x);
st_tot = tot;
while (tot>=0){
if (pri[sta[tot]] < pri[now]){///维护大根堆
update(sta[tot]);
tot--;
}
else {
ch[sta[tot]][1] = now;
break;
}
}
if (tot < st_tot) ch[now][0] = sta[tot+1];
sta[++tot] = now;
}
while (tot>=0) update(sta[tot--]);
return sta[0];
}
void insert(int pos,int tot){
int x,y;
split(root,pos,x,y);
x = merge(x,build(tot));
root = merge(x,y);
}
void remove(int pos,int tot){
int x,y,z;
split(root,pos-1,x,y);
split(y,tot,y,z);
del(y);
root = merge(x,z);
}
void update(int pos,int tot,int c){
int x,y,z;
split(root,pos-1,x,y);
split(y,tot,y,z);
use_for_update(y,c);
root = merge(x,merge(y,z));
}
void reverse(int pos,int tot){
int x,y,z;
split(root,pos-1,x,y);
split(y,tot,y,z);
use_for_reverse(y);
root = merge(x,merge(y,z));
}
int getsum(int pos,int tot){
if (pos<=0) return 0;
int x,y,z;
split(root,pos-1,x,y);
split(y,tot,y,z);
int ans = sum[y];
root = merge(x,merge(y,z));
return ans;
}
int getmcs(){
return mcs[root];
}
void del(int rt){
if (!rt) return ;
if (ch[rt][0]) del(ch[rt][0]);
if (ch[rt][1]) del(ch[rt][1]);
nextpos[ed++] = rt;
if (ed==500006) ed = 1;
}
/* 检查数组情况
void DFS(int rt){
if (!rt) return ;
push_down(rt);
if (ch[rt][0]) DFS(ch[rt][0]);
printf("now = %d,val = %d,lmax = %d,rmax = %d\n",rt,val[rt],l[rt],r[rt]);
if (ch[rt][1]) DFS(ch[rt][1]);
}
*/
}ftp;
求逆序
#include
using namespace std;
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i>1
#define tl tree[rt].l
#define tr tree[rt].r
const int N = 5e5+5;
const int maxn = 500000*32 + 5;
const int MAXR = 1e9;
struct node
{
int l,r;
int sum;
}tree[maxn];
int sz;
void init(){sz = 2;}
void push_up(int rt)
{
tree[rt].sum = tree[tl].sum + tree[tr].sum;
}
void update(int p,int& rt,int l,int r)
{
//printf("update[%d,%d]\n",l,r);
if (!rt){
rt = sz++;
tl = tr = tree[rt].sum = 0;
}
if (l==r){
tree[rt].sum++;
return;
}
mid;
if (p<=m) update(p,tl,l,m);
else update(p,tr,m+1,r);
push_up(rt);
}
ll query(int L,int R,int rt,int l,int r)
{
//printf("query[%d,%d]\n",l,r);
if (!rt) return 0;
if (L<=l && r<=R) return tree[rt].sum;
ll ans = 0;
mid;
if (L<=m) ans += query(L,R,tl,l,m);
if (R>m) ans += query(L,R,tr,m+1,r);
return ans;
}
int main()
{
init();
int n;
scanf("%d",&n);
ll ans = 0;
int root = 1;
for1(i,1,n){
int x;
scanf("%d",&x);
if (x+1<=MAXR) ans += query(x+1,MAXR,1,1,MAXR);//防止区间无效
update(x,root,1,MAXR);
}
printf("%lld\n",ans);
return 0;
}
静态区间第k小
int a[N];
int data[N];
void discrete(int n)//使用该函数把a[i]变成原本a[i]离散化后对应的数,data可以根据离散化后的值推实际的大小
{
for1(i,1,n) data[i] = a[i];
sort(data+1,data+1+n);
int cnt = unique(data+1,data+1+n) - data;
for1(i,1,n) a[i] = lower_bound(data+1,data+cnt,a[i]) - data;
}
struct node
{
int l,r;
int sum;
}tree[M];
int sz,root[N];
void push_up(int rt){
tree[rt].sum = tree[tl].sum + tree[tr].sum;
}
void update(int old,int p,int& rt,int l,int r)
{
rt = sz++;//这里容易脑抽写成if(!rt) rt = sz++
if (l==r){
tree[rt].sum = tree[old].sum + 1;
return ;
}
tree[rt] = tree[old];
mid;
if (p<=m) update(tl,p,lson);
else update(tr,p,rson);
push_up(rt);
}
int ccnt;
int query(int old,int k,int rt,int l,int r)
{
if (l==r) return data[l];
mid;
ccnt = tree[tl].sum - tree[tree[old].l].sum;
if (ccnt >= k) return query(tree[old].l,k,lson);
else return query(tree[old].r,k-ccnt,rson);
}
int main()
{
int n,m;
scanf("%d %d",&n,&m);
sz = 1;
for1(i,1,n) scanf("%d",a+i);
discrete(n);
for1(i,1,n) update(root[i-1],a[i],root[i],1,n);
int l,r,k;
for1(i,1,m){
scanf("%d %d %d",&l,&r,&k);
printf("%d\n",query(root[l-1],k,root[r],1,n));
}
return 0;
}
struct PBT
{
int ch[2];
int val;
int cnt;
int pri;
}t[N*50];
int root[N],sz,nowv;
inline int rnd(){
static int seed=703;
return seed=int(seed*48271LL%2147483647);
}
void update(int rt){
t[rt].cnt = 1 + t[t[rt].ch[0]].cnt + t[t[rt].ch[1]].cnt;
}
int newnode(int v){
t[sz].ch[0] = t[sz].ch[1] = 0;
t[sz].cnt = 1;
t[sz].pri = rnd();
t[sz].val = v;
return sz++;
}
int merge(int x,int y){
if (!x || !y) return x+y;
else {
int now = sz++;
if (t[x].pri < t[y].pri){
t[now] = t[x];
t[now].ch[1] = merge(t[now].ch[1],y);
}
else {
t[now] = t[y];
t[now].ch[0] = merge(x,t[now].ch[0]);
}
update(now);
return now;
}
}
void split(int rt,int v,int& x,int& y){
if (!rt) x = y = 0;
else {
int now = sz++;
t[now] = t[rt];
if (t[now].val<=v){
x = now;
split(t[x].ch[1],v,t[x].ch[1],y);
}
else {
y = now;
split(t[y].ch[0],v,x,t[y].ch[0]);
}
update(now);
}
}
void insert(int old,int v,int& rt){
int x,y;
split(root[old],v,x,y);
rt = merge(merge(x,newnode(v)),y);
}
void Insert(int old,int v){insert(old,v,root[nowv++]);}
void remove(int old,int v,int& rt){
int x,y,z;
split(root[old],v-1,x,y);
split(y,v,y,z);
y = merge(t[y].ch[0],t[y].ch[1]);
rt = merge(merge(x,y),z);
}
void Remove(int old,int v){remove(old,v,root[nowv++]);}
int rnk(int old,int v){
root[nowv++] = root[old];
int x,y;
split(root[old],v-1,x,y);
return t[x].cnt + 1 ;
}
int Rank(int old,int v){return rnk(old,v) -1;}
int kth(int rt,int k){
if (t[t[rt].ch[0]].cnt >= k) return kth(t[rt].ch[0],k);
k -= t[t[rt].ch[0]].cnt+1;
if (k<=0) return rt;
else return kth(t[rt].ch[1],k);
}
int Kth(int old,int k){
root[nowv++] = root[old];
return t[kth(root[old],k+1)].val;
}
int Pre(int old,int v){
root[nowv++] = root[old];
int x,y;
split(root[old],v-1,x,y);
return t[kth(x,t[x].cnt)].val;
}
int Suc(int old,int v){
root[nowv++] = root[old];
int x,y;
split(root[old],v,x,y);
return t[kth(y,1)].val;
}
void init(){
sz = nowv = 1;
root[0] = 0;
//insert(0,-2147483647,root[0]);
//insert(0,2147483647,root[0]);
}
BZOJ没了,我也没了
解决树上的链,子树修改与查询(配合线段树)
const int N = 1e5+5;
int top[N];//当前点所在链的头节点
int id[N];//区间更新时实际的区间位置
int dep[N];//深度
int fa[N];//父节点
int son[N];//重儿子节点
int size[N];//子树大小
int w[N];//节点权值
int a[N];//节点初值在线段树上对应位置的初值。a[id[i]] = w[i]
void dfs1(int now,int f,int nowdep){
dep[now] = nowdep;
fa[now] = f;
size[now] = 1;
for (int i=head[now];i!=0;i=edge[i].last){
int v = edge[i].to;
if (v==f) continue;
dfs1(v,now,nowdep+1);
size[now] += size[v];
if (size[v] > size[son[now]]) now = v;
}
}
int tot = 1;
void dfs2(int now,int topf){
id[now] = tot++;
a[id[now]] = w[now];//每个节点初始有权值,a用于更新初始权值
top[now] = topf;
if (!son[now]) return ;
dfs2(son[now],topf);
for (int i=head[now];i!=0;i=edge[i].last){
int v = edge[i].to;
if (v==fa[now] || v==son[now]) continue;
dfs2(v,v);
}
}
void interval(int x,int y){//区间修改:O(logn*logn)
while (top[x]!=top[y]){
if (dep[top[x]] < dep[top[y]]) swap(x,y);
//处理区间 [id[top[x]],id[x]]
x = fa[top[x]];
}
if (dep[x]
合并:我插你
交集:带着自己的成绩等人来救你吧
#define ll long long
ll lb[65];
bool insert(ll x){
for (int i=62;i>=0;i--){
if ((1LL<
#include
using namespace std;
#define ll long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;ival[now]];//第一遍说:这里是大于
if (now) cnt[now]++;
else {now = newnode(v,f);if (f)ch[f][v>val[f]] = now;}
splay(now);//通过splay重构了一下树,并(主要是)正确更新了原本由于新插入一个节点而产生错误的祖先节点的信息
}
void remove(int v){
rank(v);//找点并splay,因为还不知道v在哪,不知道从哪splay
if (cnt[root]>1) cnt[root]--;
else if (!ch[root][0] && !ch[root][1]) root = 0;
else if (!ch[root][0] || !ch[root][1]) root = ch[root][0]?ch[root][0]:ch[root][1],fa[root] = 0;//fa[root] = 0很关键,不然这个点旋转时会重新搞出删掉的节点
else {
int p = getpre(v,true);//保证有前驱后继,因为左右子树都有才能进这个else
int old = root;
splay(p);//这里知道点在哪儿,直接splay ,前驱直接splay到根的话,根一定在右儿子(不会三点一线,消失不见)
fa[ch[old][1]] = p;
ch[p][1] = ch[old][1];
push_up(p);
}
}
int rank(int v){
int now = root,ans = 0;
while (now){
if (val[now]>v) now = ch[now][0];
else{
ans += size[ch[now][0]];
if (val[now]==v) {splay(now);return ans+1;}//此处需要splay的原因与求前驱后继有关
ans += cnt[now];
now = ch[now][1];
}
}
}
int kth(int k){
int now = root;
while (now){
if (k<=size[ch[now][0]]) now = ch[now][0];
else {
k -= size[ch[now][0]] + cnt[now];
if (k<=0) return val[now];
else now = ch[now][1];
}
}
}
int getpre(int v,bool getid=false){//getid = true就返回前驱的下标
int ans = -INF,id = 0;
int now = root;
while (now){
if (val[now]>=v) now = ch[now][0];
else{
if (val[now]>ans) ans = val[now],id = now;
now = ch[now][1];
}
}
return getid?id:ans;
}
int getsuc(int v,bool getid=false){
int ans = INF,id = 0;
int now = root;
while (now){
if (val[now]<=v) now = ch[now][1];
else {
if (val[now]
//不保证一定没有问题
#include
using namespace std;
#define ll long long
#define for1(i,a,b) for (int i=a;i<=b;i++)
#define for0(i,a,b) for (int i=a;i=0;i--){
if (lca[x][i]!=lca[y][i]) x = lca[x][i],y = lca[y][i];
}
return lca[x][0];
}
//**************************************************************************************
int main()
{
srand(time(0));
freopen("C:/Users/DELL/Desktop/input.txt", "w", stdout);//输入想要保存文件的路径
/*u*/ //修改数据组数
int T = 100;//数据组数,可修改
printf("%d\n",T);
/*d*/
/*u*/ //修改生成的树最多节点个数,必须<=1e5
int maxsize = 20;
/*d*/
while(T--){
/*u*/ //修改当前这组数据的节点数以及询问次数,q默认10次
int n = Rand()%(maxsize+1),q=10;
if (!n) n++;
/*d*/
printf("%d %d\n",n,q);
for1(i,1,n) uf[i] = i,head[i] = 0;
memset(lca,0,sizeof lca);
id = 1;
for1(i,1,n){
if (i!=1) printf(" ");
printf("%d",Rand(true));
}puts("");//生成每个节点的值
int hulue = Rand()%(n+1);
if (!hulue) hulue++;
for1(i,1,n){
if (i==hulue) continue;
int v;
do {
v = Rand()%(n+1);
if (!v) v++;
}while (find1(i)==find1(v));
join(i,v);//并查集
printf("%d %d\n",i,v);//生成数据
add(i,v);add(v,i);//生成树,用于check合法的k
}//随机生成一棵树结束
dfs(1,0,1);
getst(n);
while(q--){
int u = Rand()%(n+1),v = Rand()%(n+1);
if (!u) u++;if (!v) v++;
int LCA = getlca(u,v);
int maxk = dep[u]-dep[LCA]+dep[v]-dep[LCA]+1;
int k = Rand()%(maxk+1);
if (!k) k++;
printf("%d %d %d\n",u,v,k);
}
}
return 0;
}
struct cp{
double x,y;
cp(double xx=0,double yy=0){x=xx;y=yy;}
double real(){return x;}
double imag(){return y;}
cp friend operator + (cp a,cp b){return cp(a.x+b.x,a.y+b.y);}
cp friend operator - (cp a,cp b){return cp(a.x-b.x,a.y-b.y);}
cp friend operator * (cp a,cp b){return cp(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
};
int rev[N];
void getrev(int n){//获取下标二进制下翻转后对应的位置的数组0~n-1
int bit = 1;
while ( (1<>1]>>1) + ((1&i)<>=1;
a = a*a%mod;
}
return ans;
}
ll getny(ll a){
return ft(a,mod-2);
}
void FFT(cp a[],int RB,int inv){
for0(i,0,RB){
if (i>1;
cp wn = cp(cos(2*pi*1/n),inv*sin(2*pi*1/n));//总共只需调用logn次三角函数,这里是TLE的关键
for (int st = 0;st < RB;st += n){
cp wnk = cp(1,0);
for0(i,st+0,st+mid){
cp x = wnk*a[i+mid];
a[i+mid] = a[i] - x;
a[i] = a[i] + x;
wnk = wnk*wn;//模长相乘 极角相加的运用
}
}
}
if (inv==-1){
for0(i,0,RB) a[i].x = (int)(a[i].real()/RB + 0.5);
}
}
void NTT(ll a[],int RB,int inv){
for0(i,0,RB) if (i (mod-1)* 1/n
if (inv==-1) wn = getny(wn);
for (int st = 0;st < RB;st += n){
ll wnk = 1;//cp(1,0) --> 1
for0(i,st+0,st+mid){
int x = wnk*a[i+mid]%mod;
a[i+mid] = (a[i] - x + mod)%mod;//减法记得加个模
a[i] = (a[i] + x)%mod;
wnk = wnk*wn%mod;
}
}
}
if (inv==-1) {
ll rbny = getny(RB);
for0(i,0,RB) a[i] = a[i]*rbny%mod;//注意这里不是/RB了
}
}