最近的一些小题题

懒得一个一个发了...

一起搞一下吧23333

(其实你这个菜b就是懒得写详细题解承认吧

bzoj4668

每次link一个递增的边权,查询两点边权最小值。

并查集按秩合并乱搞一下...本来想LCT的但是看见网上LCT被卡了一遍

#include<bits/stdc++.h>
const int maxn = 500010;
using namespace std;
int f[maxn],depth[maxn],val[maxn],rank[maxn];
int lastans,opt;
int n,m;
int dfn;
inline int find(int x)
{
    if(x == f[x])return x;
    int res = find(f[x]);
    depth[x] = depth[f[x]] + 1;
    return res;
}
inline int solve(int x,int y)
{
    int ret = -1;
    while(x != y)
    {
        if(depth[x] < depth[y]){x ^= y, y ^= x, x ^= y;}
        ret = max(ret,val[x]);
        x = f[x];
    }
    return ret;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)f[i] = i;
    while(m--)
    {
        int x,y;
        scanf("%d%d%d",&opt,&x,&y);
        x ^= lastans,y ^= lastans;
        if(opt == 0)
        {
            ++dfn;
            int fx = find(x),fy = find(y);
            if(rank[fx] >= rank[fy])
            {
                f[fy] = fx;
                val[fy] = dfn;
                if(rank[fx] == rank[fy])++rank[fx];
            }
            else
            {
                f[fx] = fy;
                val[fx] = dfn;
            }
        }
        else
        {
            int fx = find(x),fy = find(y);
            if(fx != fy)
            {
                lastans = 0;
                puts("0");
            }
            else
            {
                lastans = solve(x,y);
                printf("%d\n",lastans);
            }
        }
    }
}
View Code

bzoj2006

一个序列(n<=500000),要求选定k个不同区间,使得区间长度在L,R之间,并使得k个区间和之和最大,输出这个最大值。

本来思考了很长时间dp...

结果发现是主席树乱搞

每一个子序列的权值和可以转化为两个前缀和之差。我们考虑以每一个位置为结尾的子序列,它的权值和可以看作是以该位置为结尾的前缀和减去它前面的某个前缀和。

那么想要这个子序列的权值和尽量大,那么就要前面的那个前缀和尽可能小。如果数目不够,就第2小。再不够,就第3小。

于是我们维护一个全局堆,分别表示以每个位置为结尾的最大子序列权值和,每次取出堆顶时再放进堆中一个结尾位置相同的第k+1大的权值和。

这样k次就能出解。

这要求我们能够快速求出区间第k小,利用可持久化线段树即可。

时间复杂度O(klogn).

#include<bits/stdc++.h>
#define int long long
using namespace std;
  
const int maxn = 500010;
const int lima = -500000005;
const int limb = 500000005;
int n,k,L,R;
int a[maxn];
int s[maxn],ans;
 
inline int read()
{
    int x = 0,f = 1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch == '-') f = -f;
    for(;isdigit(ch);ch=getchar())x = 10 * x + ch-'0';
    return x*f;
}
  
namespace SegTree
{
    int ls[20000005] ,rs[20000005] , root[maxn];
    int val[20000005] ,SIZE;
    inline void insert(int pre,int &cur,int l,int r,int va)
    {
        cur = ++SIZE;
        ls[cur] = ls[pre], rs[cur] = rs[pre], val[cur] = val[pre] + 1;
        if(l == r)return;
        int mid = (l+r) >> 1;
        if(va <= mid) insert(ls[pre],ls[cur],l,mid,va);
        else insert(rs[pre],rs[cur],mid+1,r,va);
    }
    inline int query(int cur,int pre,int l,int r,int k)
    {
        if(val[pre] - val[cur] < k) return 1e9;
        if(l >= r)return l;
        int x = val[ls[pre]] - val[ls[cur]];
        int mid = (l+r) >> 1;
        if(k <= x)return query(ls[cur],ls[pre],l,mid,k);
        else return query(rs[cur],rs[pre],mid+1,r,k-x);
    }
}
  
struct data
{
    int n,k; int v;
    bool operator < (const data &b)const{return v < b.v;}
};
priority_queue<data> q;
  
signed main()
{
    using namespace SegTree;
    n=read(),k=read(),L=read(),R=read();
    insert(0,root[0],lima,limb,0);
    for(int i=1;i<=n;i++)s[i] = s[i-1] + read();
    for(int i=1;i<=n;i++)insert(root[i-1],root[i],lima,limb,s[i]);
      
    for(int i=L;i<=n;i++)
    {
        int aR = i - L ,aL = i - R - 1;
        int tem;
        if(aL < 0)tem = query(0,root[aR],lima,limb,1);
        else tem = query(root[aL],root[aR],lima,limb,1);
        q.push((data){i,1,s[i] - tem});
    }
    data temp;
    while(k--)
    {
        temp = q.top(); q.pop();
        ans += temp.v;
        int aR = temp.n - L ,aL = temp.n - R - 1;
        int tem;
        if(aL < 0)tem = query(0,root[aR],lima,limb,temp.k+1);
        else tem = query(root[aL],root[aR],lima,limb,temp.k+1);
        q.push((data){temp.n,temp.k+1,s[temp.n]-tem});
    }
    printf("%lld\n",ans);
}
View Code

bzoj3698

XWW是个影响力很大的人,他有很多的追随者。这些追随者都想要加入XWW教成为XWW的教徒。但是这并不容易,需要通过XWW的考核。
XWW给你出了这么一个难题:XWW给你一个N*N的正实数矩阵A,满足XWW性。
称一个N*N的矩阵满足XWW性当且仅当:(1)A[N][N]=0;(2)矩阵中每行的最后一个元素等于该行前N-1个数的和;(3)矩阵中每列的最后一个元素等于该列前N-1个数的和。
现在你要给A中的数进行取整操作(可以是上取整或者下取整),使得最后的A矩阵仍然满足XWW性。同时XWW还要求A中的元素之和尽量大。

这种网格的东西...首选网络流源点S向每一行连一条容量为(a[i][n],a[i][n]+1)的边
每一列向汇点T连一条容量为(a[n][i],a[n][i]+1)的边
行i向列j连一条容量为(a[i][j],a[i][j]+1)的边

先跑一遍可行流,然后删SSTT跑最大流

#include<bits/stdc++.h>
const int inf = 1000000000;
using namespace std;
 
int n,tot;
const int maxn = 600;
double a[maxn][maxn];
int first[100005],to[100005*2],next[100005*2],caps[100005*2],cnt = 1;
int dis[maxn],ind[maxn];
int S,T,SS,TT,ans;
queue<int> q;
inline void add(int u,int v,int c)
{
    to[++cnt] = v;
    next[cnt] = first[u];
    first[u] = cnt;
    caps[cnt] = c;
}
inline void ins(int u,int v,int c){add(u,v,c),add(v,u,0);}
inline int bfs(int s,int t)
{
    for(int i=1;i<=TT;i++) dis[i] = -1;
    dis[s] = 0;q.push(s);
    while(!q.empty())
    {
        int now = q.front();q.pop();
        for(int i=first[now];i;i=next[i])
        {
            if(caps[i] && dis[to[i]] == -1)
            {
                dis[to[i]] = dis[now] + 1;
                q.push(to[i]);
            }
        }
    }
    return dis[t] != -1;
}
inline int dfs(int u,int t,int flow)
{
    if(u == t)return flow;
    int w,used = 0;
    for(int i=first[u];i;i=next[i])
        if(dis[to[i]] == dis[u] + 1)
        {
            w = flow - used ,w = dfs(to[i],t,min(w,caps[i]));
            caps[i] -= w;
            caps[i^1] += w;
            used += w;
            if(used == flow)return flow; 
        }
    return used;
}
int dinic(int s,int t)
{
    ans = 0;
    while(bfs(s,t))ans += dfs(s,t,inf);
    return ans;
}
 
int main()
{
    scanf("%d",&n);
    S=2*n+1;T=S+1;SS=T+1;TT=SS+1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            scanf("%lf",&a[i][j]);
             
    for(int i=1;i<n;i++)
    {
        if(a[i][n]!=(int)a[i][n])ins(S,i,1);
        ind[S]-=(int)a[i][n];ind[i]+=(int)a[i][n];
    }
     
    for(int i=1;i<n;i++)
    {
        if(a[n][i]!=(int)a[n][i])
            ins(i+n,T,1);
        ind[i+n]-=(int)a[n][i];ind[T]+=(int)a[n][i];
    }
    for(int i=1;i<n;i++)
        for(int j=1;j<n;j++)
        {
            if(a[i][j]!=(int)a[i][j])
                ins(i,j+n,1);
            ind[i]-=(int)a[i][j];ind[j+n]+=(int)a[i][j];
        }
    for(int i=1;i<=TT;i++)
        if(ind[i]>0){tot+=ind[i];ins(SS,i,ind[i]);}
        else ins(i,TT,-ind[i]);
     
    ins(T,S,inf);
    dinic(SS,TT);
    if(tot!=ans){printf("No");return 0;}
    ans=0;
    dinic(S,T);
    printf("%d",ans*3);
    return 0;
}
View Code

bzoj1409

计算$$p^{Fib_i}(mod q)$$

欧拉定理搞一下

预处理$Fib_i % phi(q)$
然后快速幂即可

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define inf 2139062143
#define ll long long
#define MAXN 100100
using namespace std;
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
ll p[MAXN],ntp[MAXN],cnt,n,m,mod,T,m1;
struct mat {ll num[2][2];};  
mat mul(mat x,mat y)  
{  
    mat res;
    memset(res.num,0,sizeof(res.num));  
    for(ll i=0;i<2;i++)  
        for(ll j=0;j<2;j++)  
            for(ll k=0;k<2;k++)  
                (res.num[i][j]+=x.num[i][k]*y.num[k][j])%=m1;
    return res;
}
ll mat_q_pow(ll n)
{
    if(n<=0) return 0;
    mat t,res;
    memset(res.num,0,sizeof(res.num));
    t.num[0][0]=t.num[0][1]=t.num[1][0]=1,t.num[1][1]=0;
    res.num[0][0]=res.num[1][1]=1;
    while(n)
    {
        if(n&1) res=mul(res,t);
        t=mul(t,t);
        n>>=1;
    }
    return res.num[0][1];
}
void mem()
{
    for(ll i=2;i<MAXN;i++)
    {
        if(!ntp[i]) p[++cnt]=i;
        for(ll j=1;p[j]*i<MAXN;j++)
        {
            ntp[p[j]*i]=1;
            if(i%p[j]==0) break;
        }
    }
}
void phi(ll x)
{
    ll res=x;
    for(ll i=1;p[i]*p[i]<=x;i++)
        if(x%p[i]==0)
        {
            res/=p[i],res*=p[i]-1;
            while(x%p[i]==0) x/=p[i];
        }
    if(x!=1) res/=x,res*=x-1;
    m1=res;
}
ll num_q_pow(ll k,ll x)
{
    ll res=1;
    while(x)
    {
        if(x&1) (res*=k)%=mod;
        (k*=k)%=mod,x>>=1;
        //cout<<n<<" "<<res<<endl;
    }
    return res;
}
int main()
{
    T=read(),n=read();
    mem();
    while(T--)
    {
        m=read(),mod=read();
        phi(mod);
        //cout<<m1<<endl;
        //cout<<mat_q_pow(m)<<endl;
        printf("%lld\n",num_q_pow(n,mat_q_pow(m))%mod);
    }
}
View Code

bzoj2178

圆的面积并 模板

/**************************************************************
    Problem: 2178
    User: Ez3real
    Language: C++
    Result: Accepted
    Time:11208 ms
    Memory:1372 kb
****************************************************************/
 
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define fir first
#define sec second
#define eps (1e-13)
#define maxn 1010
#define four 4.0
#define six 6.0
#define two 2.0
#define re register 
using namespace std;
typedef pair<double,double>Pdd; 
typedef struct point{double x,y;}P;
typedef struct circle
{
    P o;
    double r;
    bool operator <(const circle &z)const{ return o.x<z.o.x;}
    Pdd getd(double x)  
    {  
        if(r<=fabs(o.x-x))   return Pdd(0,0);//相离 
        double dis=sqrt(r*r-(o.x-x)*(o.x-x));  
        return Pdd(o.y-dis,o.y+dis); //下、上交点 
    }  
}C;
int n,no[maxn],cntt;
double minl=10101010.0,maxr=-10101010.0;
C cir[maxn],circ[maxn];
Pdd pd[maxn];
double D(P _1,P _2){return sqrt((_1.x-_2.x)*(_1.x-_2.x)+(_1.y-_2.y)*(_1.y-_2.y));}
 
double getf(double x)
{//此线在多个圆中的部分的并 
    double ans=0,last=-10101010;  
    int cnt=0;  
    for(re int i=1;i<=cntt;i++)  
    {  
        pd[++cnt]=circ[i].getd(x);  
        cnt=(pd[cnt]==Pdd(0,0))?cnt-1:cnt;  
    }  
    sort(pd+1,pd+cnt+1);  
    for(re int i=1;i<=cnt;i++)  
    {  
        if(pd[i].fir>last)  
            ans+=pd[i].sec-pd[i].fir,last=pd[i].sec;   
        else if(pd[i].sec>last)  
            ans+=pd[i].sec-last,last=pd[i].sec;  
    }  
    //printf("***%.8lf %.8lf***",x,ans);
    return ans;  
} 
inline double getsim(double l,double r,double fl,double fm,double fr){return ((r-l)/six)*(fl+four*fm+fr);}
double simpson(double l,double m,double r,double fl,double fm,double fr)
{//是对f(x)表示直线x在圆内的部分的总长度这个函数求积分 
    double lm=(l+m)/two,rm=(m+r)/two,flm=getf(lm),frm=getf(rm);
    double lans=getsim(l,m,fl,flm,fm),rans=getsim(m,r,fm,frm,fr),totans=getsim(l,r,fl,fm,fr);
    //printf("%.8lf %.8lf %.8lf %.8lf\n",l,r,lans+rans,totans);system("pause");
    if(fabs(lans+rans-totans)<=eps)return totans;
    else return simpson(l,lm,m,fl,flm,fm)+simpson(m,rm,r,fm,frm,fr);
}
int main()
{
    memset(no,0,sizeof(no));
    scanf("%d",&n);
    for(re int i=1;i<=n;i++)
    {
        scanf("%lf%lf%lf",&cir[i].o.x,&cir[i].o.y,&cir[i].r);
        if(minl>cir[i].o.x-cir[i].r)minl=cir[i].o.x-cir[i].r;
        if(maxr<cir[i].o.x+cir[i].r)maxr=cir[i].o.x+cir[i].r;
    }
    sort(cir+1,cir+n+1);
    for(re int i=1;i<=n;i++)
    {
        if(no[i])continue;  
        for(int j=i+1;j<=n;j++)
        {
            if(no[j])continue;
            if(D(cir[i].o,cir[j].o)+cir[j].r<=cir[i].r)  
                no[j]=1;  
        }
    }
    for(re int i=1;i<=n;i++)
        if(!no[i])circ[++cntt]=cir[i];
    double ans=simpson(minl,(minl+maxr)/two,maxr,0,getf((minl+maxr)/two),0);
    printf("%.3lf",ans);
    return 0;
}
View Code

bzoj4621

最初你有一个长度为 N 的数字序列 A。为了方便起见,序列 A 是一个排列。
你可以操作最多 K 次。每一次操作你可以先选定一个 A 的一个子串,然后将这个子串的数字全部变成原来这个子串的最大值。问最终有几种可能的数字序列。答案对 1e9+7 取模。

考虑对结束后的数组dp,令dp[i][j]表示到第i位,操作了j次的方案;由于次数限制,所以对于每一个数字段最多操作一次。考虑转移,假设第i位左右可以延伸到[l,r],那么枚举延伸的右端点k,显然dp[k][j]+=Σdp[p][j-1],l-1<=p<k。注意到有个问题就是如果数组段就是[i,i],那么操作数j不能加1,要特判。

#include<bits/stdc++.h>
using namespace std;
const int p = 1e9 + 7;
int n,k;
int a[510];
int dp[510][510];
int l,r,j,cur;
//(l,r)
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    memset(dp,0,sizeof(dp));dp[0][0] = 1;
    for(int i=1;i<=n;i++)
    {
        for(l = i;a[l-1]<a[i] && l>1;l--);
        for(r = i;a[r+1]<a[i] && r<n;r++);
        (dp[i][k] += dp[i-1][k]) %= p;
        for(int opt = k;opt;opt--)
        {
            for(j=l,cur = dp[l-1][opt-1];j<=r;j++)
            {
                (dp[j][opt]+=cur) %= p;
                (cur += dp[j][opt-1]) %= p;
            }
            (dp[i][opt-1] += dp[i-1][opt-1]) %= p;
            dp[i][opt] = (dp[i][opt] - dp[i-1][opt-1] + p)%p;
        }
    }
    int sigma = 0;
    for(int i=0;i<=k;i++)(sigma += dp[n][i]) %= p;
    printf("%d\n",sigma);
}
View Code

bzoj4108

上下界

1.S -> i的出点 容量1,费用0
2.i的入点 -> T 容量1,费用0
3.i的出点 -> 0的入点 容量m,费用0
4.0的入点 -> 0的出点 容量m,费用0
5.对于边(i,j)长度为len,i -> j 容量∞,费用0

#include <cstdio>
#include <cstring>
#include <queue>
#define inf 0x3f3f3f3f
using namespace std;
queue<int> q;
int head[300] , to[100000] , val[100000] , cost[100000] , next[100000] , cnt = 1 , s , t , dis[300] , from[300] , pre[300];
void add(int x , int y , int v , int c)
{
    to[++cnt] = y , val[cnt] = v , cost[cnt] = c , next[cnt] = head[x] , head[x] = cnt;
    to[++cnt] = x , val[cnt] = 0 , cost[cnt] = -c , next[cnt] = head[y] , head[y] = cnt;
}
bool spfa()
{
    int x , i;
    memset(dis , 0x3f , sizeof(dis));
    memset(from , -1 , sizeof(from));
    dis[s] = 0 , q.push(s);
    while(!q.empty())
    {
        x = q.front() , q.pop();
        for(i = head[x] ; i ; i = next[i])
            if(val[i] && dis[to[i]] > dis[x] + cost[i])
                dis[to[i]] = dis[x] + cost[i] , from[to[i]] = x , pre[to[i]] = i , q.push(to[i]);
    }
    return ~from[t];
}
int mincost()
{
    int ans = 0 , i , k;
    while(spfa())
    {
        k = inf;
        for(i = t ; i != s ; i = from[i]) k = min(k , val[pre[i]]);
        ans += k * dis[t];
        for(i = t ; i != s ; i = from[i]) val[pre[i]] -= k , val[pre[i] ^ 1] += k;
    }
    return ans;
}
int main()
{
    int n , k , i , j , x;
    scanf("%d%d" , &n , &k);
    s = 2 * n + 2 , t = 2 * n + 3 , add(0 , 1 , k , 0);
    for(i = 2 ; i <= n + 1 ; i ++ ) scanf("%d" , &x) , add(1 , i , inf , x) , add(s , i + n , 1 , 0) , add(i , t , 1 , 0) , add(i + n , 0 , inf , 0);
    for(i = 2 ; i <= n ; i ++ )
        for(j = i + 1 ; j <= n + 1 ; j ++ )
            scanf("%d" , &x) , add(i + n , j , inf , x);
    printf("%d\n" , mincost());
    return 0;
}
View Code

bzoj3473

给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串?

广义SAM dp一下

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 2000010;
int n,q;
char ch[maxn];
string str[200010];
struct SAM
{
    int tr[maxn][26],fa[maxn],mx[maxn],len[maxn],vis[maxn],c[maxn];
    int sum[maxn];
    int last,p,np,q,nq,cnt,rt;
    SAM(){last = ++cnt; rt = 1;}
    inline void extend(int c)
    {
        p = last, np = last = ++cnt, len[np] = len[p] + 1;
        while(!tr[p][c] && p) tr[p][c] = np, p = fa[p];
        if(!p) fa[np] = rt;
        else
        {
            q = tr[p][c];
            if(len[q] == len[p] + 1) fa[np] = q;
            else
            {
                nq = ++cnt; len[nq] = len[p] + 1; memcpy(tr[nq],tr[q],sizeof(tr[q]));
                fa[nq] = fa[q]; fa[q] = fa[np] = nq;
                while(tr[p][c] == q) tr[p][c] = nq,p = fa[p];
            }
        }
    }
    inline void dfs(int x)
    {
        if(x == rt || vis[x])return;
        vis[x] = 1;dfs(fa[x]);sum[x] += sum[fa[x]];
    }
}sam;
   
int main()
{
    //freopen("data1.in","r",stdin);
    //freopen("data1.out","w",stdout);
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",ch);
        int LEN = strlen(ch); str[i] = (string)ch;
        for(int j=0;j<LEN;j++) sam.extend(ch[j]-'a');
        sam.last = sam.rt;
    }
    for(int i=1;i<=n;i++)
    {
        int LEN = str[i].length();
        int cur = sam.rt,now = i;
        for(int j=0;j<LEN;j++)
        {
            cur = sam.tr[cur][str[i][j]-'a'];
            int x=cur;
            while(x && sam.vis[x] != now)sam.vis[x] = now,sam.c[x]++,x = sam.fa[x];
        }
    }
    for(int i=1;i<=sam.cnt;i++)sam.vis[i] = 0;
    for(int i=1;i<=sam.cnt;i++)
        if(sam.c[i] >= q)sam.sum[i] = sam.len[i] - sam.len[sam.fa[i]];
    for(int i=1;i<=sam.cnt;i++)sam.dfs(i);
    for(int i=1;i<=n;i++)
    {
        int cur = 1,LEN = str[i].length();LL ans = 0LL;
        for(int j=0;j<LEN;j++) cur = sam.tr[cur][str[i][j]-'a'],ans += sam.sum[cur];
        printf("%lld ",ans);
    }
}
View Code

 bzoj1833

给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。

数位乱搞

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
long long dp[11],ans[11];
long long a,b;
long long calc(long long x)
{
    long long t=1;
    while(x>0) {t*=10;x--;}
    return t;
}
long long work(long long x)
{
    long long t=1,k=0;
    while(t<=x) {t*=10;k++;}
    t/=10;k--;
    while(t>0)
    {
        long long tmp=(x/t)%10;
        dp[0]+=x/(t*10)*calc(k);
        if(tmp==0) dp[0]-=calc(k)-x%t-1;
        else dp[tmp]+=(x/(t*10))*calc(k)+x%t+1;
        for(long long i=1;i<tmp;i++)
        {
            dp[i]+=(x/(t*10)+1)*calc(k);
        }
        for(long long i=tmp+1;i<10;i++)
        {
            dp[i]+=(x/(t*10))*calc(k);
        }
        t/=10;k--;
    }
}
int main()
{
    scanf("%lld%lld",&a,&b);
    work(b);
    for(long long i=0;i<10;i++) ans[i]=dp[i];
    memset(dp,0,sizeof(dp));
    work(a-1);
    for(long long i=0;i<9;i++) printf("%lld ",ans[i]-dp[i]);
    printf("%lld\n",ans[9]-dp[9]);
}
View Code

 bzoj4212

Trie树跑一下主席树统计一下

orz

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 2050 , TRIENODE = 2000010 , MAXNODE = (maxn << 8);
int n,m,k,ans;
int la,ra,lb,rb;
char str[TRIENODE];
inline void GetSTR()
{
    char ch;
    while(!(((ch=getchar())>='a')&&(ch<='z')));
    k=1;str[1]=(ch-'a'+ans)%26;
    while(((ch=getchar())>='a')&&(ch<='z'))str[++k]=(ch-'a'+ans)%26;
}
int pa[maxn],pb[maxn],first[TRIENODE],to[maxn],next[maxn],cnt;
inline void add(int u,int v){to[++cnt]=v,next[cnt]=first[u],first[u]=cnt;}
struct Trie
{
    int tr[TRIENODE][26],st[TRIENODE],ed[TRIENODE],SIZE,dfn,rt;
    Trie(){rt = 0;}
    inline int extend()
    {
        int now = 0;
        for(int i=1;i<=k;i++)
        {
            int p = str[i];
            if(!tr[now][p]) tr[now][p] = ++SIZE;
            now = tr[now][p];
        }
        return now;
    }
    inline int dnetxe()
    {
        int now = 0;
        for(int i=k;i;i--)
        {
            int p = str[i];
            if(!tr[now][p]) tr[now][p] = ++SIZE;
            now = tr[now][p];
        }
        return now;
    }
    inline void dfs(int x)
    {
        st[x] = ++dfn;
        for(int i=0;i<26;i++)
            if(tr[x][i]) dfs(tr[x][i]);
        ed[x] = dfn;
    }
    inline void query()
    {
        int x=0;
        for(int i=1;i<=k;i++)
        {
            int p=str[i];
            if(!tr[x][p]){la=ra=0;return;}
            x=tr[x][p];
        }
        la=st[x],ra=ed[x];
    }
    inline void yreuq()
    {
        int x=0;
        for(int i=k;i;i--)
        {
            int p=str[i];
            if(!tr[x][p]){lb=rb=0;return;}
            x=tr[x][p];
        }
        lb=st[x],rb=ed[x];
    }
}A,B;
 
namespace ChairmanTree
{
    int root[TRIENODE],ls[MAXNODE],rs[MAXNODE],val[MAXNODE],sz;
    inline void insert(int &cur,int l,int r,int pre,int va)
    {
        cur = ++sz; val[cur] = val[pre] + 1;
        if(l == r)return;
        ls[cur] = ls[pre],rs[cur] = rs[pre];
        int mid = (l + r) >> 1;
        if(va <= mid) insert(ls[cur],l,mid,ls[pre],va);
        else insert(rs[cur],mid+1,r,rs[pre],va);
    }
    inline int query(int x,int l,int r)
    {
        if(!x) return 0;
        if(lb <= l && r <= rb)return val[x];
        int mid = (l+r) >> 1, ans = 0;
        if(lb <= mid)ans+=query(ls[x],l,mid);
        if(rb > mid)ans+=query(rs[x],mid+1,r);
        return ans;
    }
}
 
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        GetSTR();
        pa[i] = A.extend();
        pb[i] = B.dnetxe();
    }
    A.dfs(A.rt),B.dfs(B.rt);
    int j;
    for(int i=1;i<=n;i++)add(A.st[pa[i]],B.ed[pb[i]]);
    for(int i=1;i<=A.dfn;i++)
        for(ChairmanTree::root[i]=ChairmanTree::root[i-1],j=first[i];j;j=next[j])
            ChairmanTree::insert(ChairmanTree::root[i],1,B.dfn,ChairmanTree::root[i-1],to[j]);
    scanf("%d",&m);
    while(m --)
    {
        GetSTR();A.query();
        GetSTR();B.yreuq();
        if(la && lb) ans = ChairmanTree::query(ChairmanTree::root[ra],1,B.dfn)-ChairmanTree::query(ChairmanTree::root[la],1,B.dfn);else ans = 0;
        printf("%d\n",ans);
    }
}
View Code

bzoj1316

n个点带权有根树 每次询问是否有一条长度为Len的路径

点分治裸题

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 10005
 
int n,x,y,z,m,sum,root;
int q[N],ans[N];
int tot,point[N],nxt[N*2],v[N*2],c[N*2];
int size[N],big[N],d[N],deep[N];
bool vis[N];
 
void add(int x,int y,int z)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
}
void getroot(int x,int fa)
{
    size[x]=1;big[x]=0;
    for (int i=point[x];i;i=nxt[i])
        if (v[i]!=fa&&!vis[v[i]])
        {
            getroot(v[i],x);
            size[x]+=size[v[i]];
            big[x]=max(big[x],size[v[i]]);
        }
    big[x]=max(big[x],sum-size[x]);
    if (big[x]<big[root]) root=x;
}
void getdeep(int x,int fa)
{
    deep[++deep[0]]=d[x];
    for (int i=point[x];i;i=nxt[i])
        if (v[i]!=fa&&!vis[v[i]])
        {
            d[v[i]]=d[x]+c[i];
            getdeep(v[i],x);
        }
}
int findl(int l,int r,int k)
{
    int ans=0;
    while (l<=r)
    {
        int mid=(l+r)>>1;
        if (deep[mid]==k) ans=mid,r=mid-1;
        else if (deep[mid]<k) l=mid+1;
        else r=mid-1;
    }
    return ans;
}
int findr(int l,int r,int k)
{
    int ans=-1;
    while (l<=r)
    {
        int mid=(l+r)>>1;
        if (deep[mid]==k) ans=mid,l=mid+1;
        else if (deep[mid]<k) l=mid+1;
        else r=mid-1;
    }
    return ans;
}
int calc(int x,int now,int k)
{
    d[x]=now;deep[0]=0;
    getdeep(x,0);
    sort(deep+1,deep+deep[0]+1);
    int t=0;
    for (int i=1;i<=deep[0];++i)
    {
        if (deep[i]+deep[i]>k) break;
        int l=findl(i,deep[0],k-deep[i]);
        int r=findr(i,deep[0],k-deep[i]);
        t+=r-l+1;
    }
    return t;
}
void dfs(int x)
{
    for (int i=1;i<=m;++i)
        ans[i]+=calc(x,0,q[i]);
    vis[x]=1;
    for (int i=point[x];i;i=nxt[i])
        if (!vis[v[i]])
        {
            for (int j=1;j<=m;++j)
                ans[j]-=calc(v[i],c[i],q[j]);
            sum=size[v[i]];root=0;
            getroot(v[i],0);
            dfs(root);
        }
}
 
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<n;++i)
    {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z),add(y,x,z);
    }
    for (int i=1;i<=m;++i) scanf("%d",&q[i]);
    big[0]=N;
    sum=n;root=0;
    getroot(1,0);
    dfs(root);
    for (int i=1;i<=m;++i)
        if (ans[i]) puts("Yes");
        else puts("No");
}
View Code

bzoj3027

生成函数

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<vector>
#define pb push_back
#define mp make_pair
#define xx first
#define yy second
#define rep(i,a,b) for(int i=(a),i##_end_=(b);i<=i##_end_;i++)
#define dwn(i,a,b) for(int i=(a),i##_end_=(b);i>=i##_end_;i--)
using namespace std;
const int Size=1<<16;
char buffer[Size],*head,*tail;
inline char Getchar() {
    if(head==tail) {
        int l=fread(buffer,1,Size,stdin);
        tail=(head=buffer)+l;
    }
    if(head==tail) return -1;
    return *head++;
}
inline int read() {
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
typedef long long ll;
typedef pair<int,int> pii;
const int maxn=10000010;
const int mod=2004;
int n,F[maxn],G[maxn],l,r;
int main() {
    n=read();l=read();r=read();
    int *f=F,*g=G;
    int s=0;f[0]=1;
    rep(i,1,n) {
        int x=read();
        rep(j,0,s+x) {
            if(j) (f[j]+=f[j-1])%=mod;
            g[j]=f[j];
            if(j>x) g[j]=(g[j]-f[j-x-1]+mod)%mod;
        }
        s+=x;
        swap(f,g);
    }
    int ans=0;
    rep(i,l,r) (ans+=f[i])%=mod;
    printf("%d\n",ans);
    return 0;
}
View Code

bzoj1641

求两个点之间最大路径的最小值

FLOYD

#include<bits/stdc++.h>
using namespace std;
const int inf = 2147483233;
const int maxn = 310;
int n,m,t;
int gr[maxn][maxn];
 
int main()
{
    scanf("%d%d%d",&n,&m,&t);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)gr[i][j] = inf;
     
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        gr[u][v] = min(gr[u][v],w);
    }
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                gr[i][j] = min(gr[i][j],max(gr[i][k],gr[k][j]));
    for(int i=1;i<=t;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        if(gr[u][v] == inf)puts("-1");
        else printf("%d\n",gr[u][v]);
    }
}
View Code

bzoj2120

带修改莫队入门题

调一年系列

#include<bits/stdc++.h>
#define N 10005
using namespace std;
int qid=0,tim=0,cx[1000005],res,block[N],ans[N],a[N];
struct ask{int l,r,t,id;}q[N];
struct chan{int x,y;}ch[1005];
int cmp(ask a,ask b)
{
    if (block[a.l]==block[b.l])
    {
        if (a.r==b.r) return a.t<b.t;
        else return a.r<b.r;
    }else return a.l<b.l;
}
void change(int now,int i)
{
    if (ch[now].x>=q[i].l && ch[now].x<=q[i].r) 
    {
        cx[a[ch[now].x]]--;
        if (cx[a[ch[now].x]]==0) res--;
        if (cx[ch[now].y]==0) res++;
        cx[ch[now].y]++;            
    }
    swap(a[ch[now].x],ch[now].y);
}
int main()
{
    int n,m,i;
    scanf("%d%d",&n,&m);
    int size=sqrt(n);
    for (i=1;i<=n;i++) scanf("%d",&a[i]),block[i]=(i-1)/size+1;
    for (i=1;i<=m;i++)
    {
        int l,r;char s[5];
        scanf("%s%d%d",s,&l,&r);
        if (s[0]=='Q'){
            q[++qid].l=l; q[qid].r=r; q[qid].id=qid; q[qid].t=tim;
        }
        else ch[++tim].x=l,ch[tim].y=r;
    }
    sort(q+1,q+qid+1,cmp);
    int l=0,r=0,now=0;
    for (i=1;i<=qid;i++)
    {
        while (l<q[i].l){cx[a[l]]--;if (cx[a[l]]==0) res--; l++;}
        while (l>q[i].l){l--;if (cx[a[l]]==0) res++; cx[a[l]]++;}
        while (r<q[i].r){r++;if (cx[a[r]]==0) res++; cx[a[r]]++;}
        while (r>q[i].r){cx[a[r]]--;if (cx[a[r]]==0) res--; r--;}
        while (now<q[i].t){now++;change(now,i);}
        while (now>q[i].t){change(now,i);now--;}
        ans[q[i].id]=res;
    }
    for (i=1;i<=qid;i++)  printf("%d\n",ans[i]);
}
View Code

bzoj4999

给您一颗树,每个节点有个初始值。
现在支持以下两种操作:
1. C i x(0<=x<2^31) 表示将i节点的值改为x。
2. Q i j x(0<=x<2^31) 表示询问i节点到j节点的路径上有多少个值为x的节点。
dfs序+主席树
#include<bits/stdc++.h>
using namespace std;
 
const int maxn = 100010;
const int MAXNODE = (maxn << 7);
map<int,int> ma;
int n,q;
int first[maxn],to[maxn*2],next[maxn*2],cnt;
int val[maxn];
inline void add(int u,int v){to[++cnt]=v,next[cnt]=first[u],first[u]=cnt;}
int depth[maxn],size[maxn],top[maxn],pos[maxn],fa[maxn],dfn;
inline void dfs1(int u)
{
    size[u]=1;
    for(int i=first[u];i;i=next[i])
    {
        if(to[i] == fa[u])continue;
        fa[to[i]] = u;
        depth[to[i]] = depth[u] + 1;
        dfs1(to[i]);
        size[u] += size[to[i]];
    }
}
inline void dfs2(int u,int col)
{
    pos[u] = ++dfn;
    top[u] = col;
    int k=0;
    for(int i=first[u];i;i=next[i])
        if(to[i] != fa[u] && size[to[i]] > size[k])k=to[i];
    if(!k) return;
    dfs2(k,col);
    for(int i=first[u];i;i=next[i])
        if(to[i] != fa[u] && to[i] != k)dfs2(to[i],to[i]);
}
int ls[MAXNODE],rs[MAXNODE],root[MAXNODE],SZ,sum[MAXNODE];
inline void insert(int &cur,int l,int r,int ps,int va)
{
    if(!cur) cur = ++SZ;
    sum[cur] += va;
    if(l == r)return;
    int mid = (l + r) >> 1;
    if(ps <= mid) insert(ls[cur],l,mid,ps,va);
    else insert(rs[cur],mid+1,r,ps,va);
}
inline int query(int cur,int l,int r,int ql,int qr)
{
    if(!cur) return 0;
    if(ql <= l && r <= qr)return sum[cur];
    int mid = (l + r) >> 1 , ans = 0;
    if(ql <= mid) ans += query(ls[cur],l,mid,ql,qr);
    if(qr > mid) ans += query(rs[cur],mid+1,r,ql,qr);
    return ans;
}
inline int get(int x,int y,int col)
{
    int ans = 0;
    while(top[x] != top[y])
    {
        if(depth[top[x]] < depth[top[y]])swap(x,y);
        ans += query(root[col],1,n,pos[top[x]],pos[x]);
        x = fa[top[x]];
    }
    if(depth[x] > depth[y])swap(x,y);
    ans += query(root[col],1,n,pos[x],pos[y]);
    return ans;
}
int POS;
char opt[5];
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)scanf("%d",&val[i]);
    for(int i=1;i<n;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }
    dfs1(1),dfs2(1,1);
    for(int i=1;i<=n;i++)
    {
        if(!ma[val[i]]) ma[val[i]] = ++POS;
        insert(root[ma[val[i]]],1,n,pos[i],1);
    }
    int a,x,c;
    while(q--)
    {
        scanf("%s",opt);
        if(opt[0] == 'C')
        {
            scanf("%d%d",&a,&x);
            insert(root[ma[val[a]]],1,n,pos[a],-1);
            if(!ma[x])ma[x] = ++POS;
            insert(root[ma[x]],1,n,pos[a],1);
            val[a] = x;
        }
        else
        {
            scanf("%d%d%d",&a,&x,&c);
            if(!ma[c])puts("0");
            else printf("%d\n",get(a,x,ma[c]));
        }
    }
}
View Code

 bzoj4025

神犇有一个n个节点的图。因为神犇是神犇,所以在T时间内一些边会出现后消失。神犇要求出每一时间段内这个图是否是二分图。这么简单的问题神犇当然会做了,于是他想考考你。

二分图定义...md

可以考虑用LCT维护一个以删除时间为关键字的最大生成树 
同时维护一个边的集合表示在这个集合中的边如果在图中则一定不是二分图(就用一个数组就好了_ (:зゝ∠) _) 
由二分图的定义可知 二分图是没有奇环的图 
然后对于那两种操作: 
对于插入,如果边的两端点不连通,连边后一定不会形成环,直接link上 
如果已经联通,加边后一定存在非树边.此时如果加边后会形成奇环,就把这条边加入上述集合 
删除时候对于树边直接删除,非树边在集合中则直接从集合中删除

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 100010
#define GET (ch>='0'&&ch<='9')
#define MAXINT 0x3f3f3f3f
using namespace std;
int n,m,T,Top,cnt;
int sta[MAXN<<1],top;
int In[MAXN<<1],on[MAXN<<1];
struct splay
{
    int ch[2],fa,minn,st,sum,val;
    bool rev;
}tree[MAXN<<2];
inline void in(int &x)
{
    char ch=getchar();x=0;
    while (!GET)    ch=getchar();
    while (GET) x=x*10+ch-'0',ch=getchar();
}
struct edge
{
    int u,v,w;
}e[MAXN<<1];
struct Edge
{
    int to;
    Edge *next;
}E[MAXN<<2],*prev1[MAXN],*prev2[MAXN];
inline void insert1(int u,int v)    {E[++Top].to=v;E[Top].next=prev1[u];prev1[u]=&E[Top];}
inline void insert2(int u,int v)    {E[++Top].to=v;E[Top].next=prev2[u];prev2[u]=&E[Top];}
inline bool is_root(int x)
{
    return tree[tree[x].fa].ch[0]!=x&&tree[tree[x].fa].ch[1]!=x;
}
inline void push_down(int x)
{
    if (tree[x].rev)
    {
        tree[tree[x].ch[0]].rev^=1,tree[tree[x].ch[1]].rev^=1;
        swap(tree[x].ch[0],tree[x].ch[1]);
    }
    tree[x].rev=0;
}
inline void push_up(int x)
{
    tree[x].minn=tree[x].val;tree[x].st=x;tree[x].sum=x>n;
    if (tree[x].ch[0])
    {
        if (tree[tree[x].ch[0]].minn<tree[x].minn)  tree[x].minn=tree[tree[x].ch[0]].minn,tree[x].st=tree[tree[x].ch[0]].st;
        tree[x].sum+=tree[tree[x].ch[0]].sum;
    }
    if (tree[x].ch[1])
    {
        if (tree[tree[x].ch[1]].minn<tree[x].minn)  tree[x].minn=tree[tree[x].ch[1]].minn,tree[x].st=tree[tree[x].ch[1]].st;
        tree[x].sum+=tree[tree[x].ch[1]].sum;
    }
}
inline void rot(int x)
{
    int y=tree[x].fa,z=tree[y].fa,l,r;
    l=(tree[y].ch[1]==x);r=l^1;
    if (!is_root(y))    tree[z].ch[tree[z].ch[1]==y]=x;
    tree[tree[x].ch[r]].fa=y;tree[y].fa=x;tree[x].fa=z;
    tree[y].ch[l]=tree[x].ch[r];tree[x].ch[r]=y;
    push_up(y);push_up(x);
}
inline void Splay(int x)
{
    top=0;sta[++top]=x;
    for (int i=x;!is_root(i);i=tree[i].fa)  sta[++top]=tree[i].fa;
    while (top) push_down(sta[top--]);
    while (!is_root(x))
    {
        int y=tree[x].fa,z=tree[y].fa;
        if (!is_root(y))
        {
            if ((tree[y].ch[0]==x)^(tree[z].ch[0]==y))  rot(x);
            else    rot(y);
        }
        rot(x);
    }
}
inline void access(int x)
{
    for (int i=0;x;i=x,x=tree[x].fa)    Splay(x),tree[x].ch[1]=i,push_up(x);
}
inline void make_root(int x)
{
    access(x);Splay(x);tree[x].rev^=1;
}
inline void link(int x,int y)
{
    make_root(x);tree[x].fa=y;
}
inline void cut(int x,int y)
{
    make_root(x);access(y);Splay(y);tree[y].ch[0]=tree[x].fa=0;push_up(y);
}
inline void split(int x,int y)
{
    make_root(x);access(y);Splay(y);
}
inline int find_root(int x)
{
    /*access(x);Splay(x);
    while (tree[x].ch[0])   x=tree[x].ch[0];*/
    for (access(x),Splay(x);tree[x].ch[0];x=tree[x].ch[0]);
    return x;
}
inline void ins(int x)
{
    int u=e[x].u,v=e[x].v;
    if (u==v)   {In[x]=1;cnt++;return;}
    if (find_root(u)!=find_root(v)) on[x]=1,link(u,x+n),link(v,x+n);
    else
    {
        split(u,v);int y=tree[v].st-n;
        if (e[y].w<e[x].w)
        {
            if (tree[v].sum&1^1)    In[y]=1,cnt++;
            cut(e[y].u,y+n);cut(e[y].v,y+n);link(u,x+n);link(v,x+n);
            on[y]=0;on[x]=1;
        }
        else
        if (tree[v].sum&1^1)    In[x]=1,cnt++;
    }
}
inline void del(int x)
{
    if (on[x])  cut(e[x].u,x+n),cut(e[x].v,x+n);
    else    if (In[x])  cnt--;
}
int main()
{
    in(n);in(m);in(T);int s,t;
    for (int i=1;i<=n;i++)  tree[i].val=tree[i].minn=MAXINT,tree[i].st=i;
    for (int i=1;i<=m;i++)
    {
        in(e[i].u);in(e[i].v);in(s);in(t);e[i].w=t;
        insert1(s,i);insert2(t,i);
        tree[i+n].val=tree[i+n].minn=t;tree[i+n].st=i+n;tree[i+n].sum=1;
    }
    for (int x=0;x<T;x++)
    {
        for (Edge *i=prev1[x];i;i=i->next)  ins(i->to);
        for (Edge *i=prev2[x];i;i=i->next)  del(i->to);
        puts(cnt?"No":"Yes");
    }
}
View Code

bzoj3932

主席树裸题

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define maxn 200005
#define maxm 8000005
using namespace std;
int m,n,tot;
int rt[maxn],ls[maxm],rs[maxm];
int f[maxn],fp[maxn];
ll cnt[maxm],sum[maxm];
struct data{int s,e,p,num;}a[maxn];
struct poi{int tim,val,flg;}b[maxn*2];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline bool cmpa(data x,data y){return x.p<y.p;}
inline bool cmpb(poi x,poi y){return x.tim<y.tim;}
inline void build(int &k,int l,int r)
{
    k=++tot;sum[k]=cnt[k]=0;
    if (l==r) return;
    int mid=(l+r)>>1;
    build(ls[k],l,mid);
    build(rs[k],mid+1,r);
}
inline void update(int x,int &y,int l,int r,int v,int d)
{
    y=++tot;
    cnt[y]=cnt[x]+d;sum[y]=sum[x]+(ll)f[v]*(ll)d;
    if (l==r) return;
    ls[y]=ls[x];rs[y]=rs[x];
    int mid=(l+r)>>1;
    if (v<=mid) update(ls[x],ls[y],l,mid,v,d);
    else update(rs[x],rs[y],mid+1,r,v,d);
}
inline ll query(int k,int num,int l,int r)
{
    if (num==cnt[k]) return sum[k];
    if (l==r) return sum[k]/cnt[k]*num;
    int mid=(l+r)>>1;
    if (num<=cnt[ls[k]]) return query(ls[k],num,l,mid);
    else return sum[ls[k]]+query(rs[k],num-cnt[ls[k]],mid+1,r);
}
int main()
{
    m=read();n=read();
    F(i,1,m){a[i].s=read();a[i].e=read();a[i].p=read();a[i].num=i;}
    sort(a+1,a+m+1,cmpa);
    int sz=0;
    F(i,1,m)
    {
        if (i==1||a[i].p!=a[i-1].p) f[++sz]=a[i].p;
        fp[i]=sz;
    }
    F(i,1,m)
    {
        b[i*2-1]=(poi){a[i].s,fp[i],1};
        b[i*2]=(poi){a[i].e+1,fp[i],-1};
    }
    sort(b+1,b+m*2+1,cmpb);
    build(rt[0],1,n);
    int t=1;
    F(i,1,n)
    {
        int pre=rt[i-1],tmp=rt[i-1];
        for(;t<=2*m&&b[t].tim==i;t++)
        {
            update(pre,tmp,1,n,b[t].val,b[t].flg);
            pre=tmp;
        }
        rt[i]=tmp;
    }
    ll pr=1;
    F(i,1,n)
    {
        ll xi=read(),ai=read(),bi=read(),ci=read();
        ll ki=(ai*pr+bi)%ci+1;
        if (ki>cnt[rt[xi]]) pr=sum[rt[xi]];
        else if (ki==0) pr=0;
        else pr=query(rt[xi],ki,1,n);
        printf("%lld\n",pr);
    }
    return 0;
}
View Code

 bzoj4566

给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两
个子串中有一个位置不同。
随便写个SAM预处理一下Right集合大小
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn = 400010;
char s[maxn];
int c[maxn],size[maxn],hsh[maxn],laz[maxn];
namespace SAM
{
    int fa[maxn],to[maxn][26],mx[maxn];
    int last=1,rt=1,cnt=1;
    void extend(int c)
    {
        int q,nq;
        int p=last,np=last=++cnt;
        mx[np]=mx[p]+1;size[np]=1;
        for(;!to[p][c];p=fa[p])to[p][c]=np;
        if(!p) fa[np]=1;
        else
        {
            q=to[p][c];
            if(mx[p] + 1 == mx[q])fa[np]=q;
            else
            {
                nq=++cnt;
                mx[nq]=mx[p]+1;
                fa[nq]=fa[q];fa[q]=fa[np]=nq;
                memcpy(to[nq],to[q],sizeof(to[nq]));
                for(;to[p][c] == q;p=fa[p])to[p][c]=nq;
            }
        }
    }
}
using namespace SAM;
int main()
{
    scanf("%s",s);
    for(int i=0;s[i];i++)extend(s[i]-'a');
    scanf("%s",s);
    int p=1,len=0;LL ans=0;
    for(int i=1;i<=cnt;i++)c[mx[i]]++;
    for(int i=1;i<=cnt;i++)c[i]+=c[i-1];
    for(int i=cnt;i>=1;i--)hsh[c[mx[i]]--]=i;
    for(int i=cnt;i>=1;i--)size[fa[hsh[i]]] += size[hsh[i]];
    for(int i=0;s[i];i++)
    {
        int c=s[i]-'a';
        if(to[p][c])len++,p=to[p][c];
        else
        {
            while(p && !to[p][c])p=fa[p];
            if(p) len=mx[p]+1,p=to[p][c];
            else p=1,len=0;
        }
        ans+=(LL)size[p]*(len-mx[fa[p]]);
        laz[fa[p]]++;
    }
    for(int i=cnt;i>=1;i--)
    {
        int x=hsh[i];laz[fa[x]]+=laz[x];
        ans+=(LL)laz[x]*size[x]*(mx[x]-mx[fa[x]]);
    }
    printf("%lld",ans);
}
View Code

 

posted @ 2018-03-09 20:04  探险家Mr.H  阅读(376)  评论(6编辑  收藏  举报