2
4
2016
0

FFT是什么?

FFT,快速傅里叶变换。听说是一个可以在nlogn的复杂度内解决多项式乘法的东西,就是求类似于c[k]=sigma(a[i]*b[k-i])这样的式子,听说这样的式子叫卷积。

迪克李教导我们只要大概知道他在干什么和大致的原理就可以喽,然后就把代码背下来就可以了。听说这很兹磁。

那么既然背代码,当然要挑好的背。听说迪克李给了我们策爷的模板。

struct cp{double x,y;};//这定义的是一个复数的形式,x表示实数部分系数,y表示虚数部分系数
cp a[N],b[N],cur[N];
cp operator *(cp x,cp y){return (cp){x.x*y.x-x.y*y.y,x.x*y.y+x.y*y.x};}
cp operator +(cp x,cp y){return (cp){x.x+y.x,x.y+y.y};}
cp operator -(cp x,cp y){return (cp){x.x-y.x,x.y-y.y};}
void fft(cp *a,int n,int fl)//fl=1/-1,1表示把系数转成点对,-1表示把点对转成系数
{
    for(int i=n>>1,j=1;j<n;j++)
    {
        if (i<j) swap(a[i],a[j]);
        int k=n>>1;
        for (;k&i;i^=k,k>>=1);i^=k;
    }
    for(int m=2;m<=n;m<<=1)
    {
        cp w=(cp){cos(2*pi*fl/m),sin(2*pi*fl/m)};
        cur[0]=(cp){1,0};
        for (int i=1;i<m;i++) cur[i]=cur[i-1]*w;
        for (int i=0;i<n;i+=m)
            for (int j=i;j<i+(m>>1);++j)
            {
                cp u=a[j],v=a[j+(m>>1)]*cur[j-i];
                a[j]=u+v;
                a[j+(m>>1)]=u-v;
            }
    }
}
Category: FFT | Tags:
1
23
2016
0

【bzoj3932】【CQOI2015】任务查询系统

3932: [CQOI2015]任务查询系统

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 943  Solved: 345
[Submit][Status][Discuss]

Description

最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分。
超级计算机中的任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束(第Si秒和Ei秒任务也在运行),其优先级为Pi。同一时间可能有多个任务同时执行,它们的优先级可能相同,也可能不同。调度系统会经常向查询系统询问,第Xi秒正在运行的任务中,优先级最小的Ki个任务(即将任务按照优先级从小到大排序后取前Ki个)的优先级之和是多少。特别的,如果Ki大于第Xi秒正在运行的任务总数,则直接回答第Xi秒正在运行的任务优先级之和。上述所有参数均为整数,时间的范围在1到n之间(包含1和n)。
 

Input

输入文件第一行包含两个空格分开的正整数m和n,分别表示任务总数和时间范围。
接下来m行,每行包含三个空格分开的正整数Si、Ei和Pi(Si≤Ei),描述一个任务。 
接下来n行,每行包含四个空格分开的整数Xi、Ai、Bi和Ci,描述一次查询。查询的参数Ki需要由公式 Ki=1+(Ai*Pre+Bi) mod Ci
计算得到。其中Pre表示上一次查询的结果,对于第一次查询,Pre=1。
 

Output

输出共n行,每行一个整数,表示查询结果。
 

Sample Input

4 3
1 2 6
2 3 3
1 3 2
3 3 4
3 1 3 2
1 1 3 4
2 2 4 3

Sample Output

2
8
11

HINT

 

样例解释

K1 = (1*1+3)%2+1 = 1

K2 = (1*2+3)%4+1 = 2

K3 = (2*8+4)%3+1 = 3

对于100%的数据,1≤m,n,Si,Ei,Ci≤100000,0≤Ai,Bi≤100000,1≤Pi≤10000000,Xi为1到n的一个排列
 

我们对每个时间建一颗线段树,线段树上的区间表示优先级,每个区间记录任务个数和优先级总和。时间i只要在时间i-1的基础上,加上开始时间为i的任务,减去结束时间为i-1的任务,这道题就A啦、、、

代码:

 

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
struct node{ll l,r,num,sum;}tr[10000010];
ll n,m,cnt,n1,a[200010],b[200010],c[200010],d[200010],P[200010],root[200010];
ll edgenum,vet[200010],next[200010],head[200010];
ll t,A,B,C,k,ans;
bool cmp(ll x,ll y){return c[x]<c[y];}
void add(ll u,ll v)
{
    vet[++edgenum]=v;
    next[edgenum]=head[u];
    head[u]=edgenum;
}
void change(ll &p,ll l,ll r,ll x,ll val)
{
    tr[++cnt]=tr[p];p=cnt;
    if (val>0){tr[p].num++;tr[p].sum+=c[P[val]];}else{tr[p].num--;tr[p].sum-=c[P[-val]];}
    if (l==r) return;
    ll mid=(l+r)>>1;
    if (x<=mid) change(tr[p].l,l,mid,x,val);else change(tr[p].r,mid+1,r,x,val);
}
ll ask(ll p,ll l,ll r,ll k)
{
    if (l==r) return tr[p].sum;
    ll mid=(l+r)>>1,tmp=tr[tr[p].l].num;
    if (k<=tmp) return ask(tr[p].l,l,mid,k);else return tr[tr[p].l].sum+ask(tr[p].r,mid+1,r,k-tmp);
}
int main()
{
    scanf("%lld%lld",&n,&m);
    for (ll i=1;i<=n;i++)
    {
        scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
        P[i]=i;
    }
    sort(P+1,P+n+1,cmp);
    for (ll i=1;i<=n;i++) d[P[i]]=i;
    for (ll i=1;i<=n;i++){add(a[i],d[i]);add(b[i]+1,-d[i]);}
    for (ll i=1;i<=m;i++)
    {
        root[i]=root[i-1];
        for (ll e=head[i];e;e=next[e])
            change(root[i],1,m,(vet[e]>0)? vet[e]:-vet[e],vet[e]);
    }
    ans=1;
    for (ll i=1;i<=m;i++)
    {
        scanf("%lld%lld%lld%lld",&t,&A,&B,&C);
        ll k=1+(A*ans+B)%C;
        k=min(k,tr[root[t]].num);
        ans=ask(root[t],1,m,k);
        printf("%lld\n",ans);
    }
}
Category: 主席树 | Tags:
1
22
2016
0

【bzoj2724】【Violet 6】蒲公英

2724: [Violet 6]蒲公英

Time Limit: 40 Sec  Memory Limit: 512 MB
Submit: 1182  Solved: 389
[Submit][Status][Discuss]

Description

 

Input

修正一下

l = (l_0 + x - 1) mod n + 1, r = (r_0 + x - 1) mod n + 1

Output

Sample Input

6 3
1 2 3 2 1 2
1 5
3 6
1 5

Sample Output

1
2
1

HINT

 


修正下:


n <= 40000, m <= 50000

 

Source

大家可以先看一下陈立杰的论文《区间众数解题报告》,这道题是没有修改的情况。我们只要用分块大法做就可以了。

附:陈立杰论文截取

代码:

 

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int n,q,m,x,y,ans,Max,L,R,len,num;
int a[40010],p[40010],b[40010],c[40010],le[40010],ri[40010],sum[40010];
int l[210],r[210],f[210][210],g[210][210];
int get(int l,int r,int x)
{return upper_bound(c+le[x],c+ri[x]+1,r)-lower_bound(c+le[x],c+ri[x]+1,l);}
void ask(int l,int r,int x,int y)
{
	for (int i=x;i<=y;i++)
	{
		int cnt=get(l,r,a[i]);
		if (cnt>Max||(cnt==Max&&a[i]<ans)){ans=a[i];Max=cnt;}
	}
}
int main()
{
	scanf("%d%d",&n,&q);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		p[i]=a[i];
	}
	m=n;
	sort(p+1,p+m+1);
	m=unique(p+1,p+m+1)-p-1;
	for (int i=1;i<=n;i++)
		a[i]=lower_bound(p+1,p+m+1,a[i])-p;
	for (int i=1;i<=n;i++)
		b[a[i]]++;
	for (int i=1;i<=m;i++)
		b[i]+=b[i-1];
	for (int i=1;i<=m;i++){le[i]=b[i-1]+1;ri[i]=b[i-1];}
	for (int i=1;i<=n;i++) c[++ri[a[i]]]=i;
	len=(int)sqrt(n);
	num=n/len;if (n%len) num++;
	for (int i=1;i<=num;i++){l[i]=(i-1)*len+1;r[i]=min(i*len,n);}
	for (int i=1;i<=num;i++)
	{
		x=y=0;
		memset(sum,0,sizeof(sum));
		for (int j=i;j<=num;j++)
		{
			for (int k=l[j];k<=r[j];k++)
			{
				sum[a[k]]++;
				if (sum[a[k]]>y||(sum[a[k]]==y&&a[k]<x)){x=a[k];y=sum[a[k]];}
			}
			f[i][j]=x;g[i][j]=y;
		}
	}
	ans=0;
	while (q--)
	{
		scanf("%d%d",&x,&y);
		x=(x+ans-1)%n+1;y=(y+ans-1)%n+1;
		if (x>y) swap(x,y);
		L=x/len;if (r[L]<x) L++;
		R=y/len;if (r[R]<y) R++;
		ans=0;Max=0;
		if (L==R) {ask(x,y,x,y);ans=p[ans];printf("%d\n",ans);continue;}
		if (R-1>=L+1){ans=f[L+1][R-1];Max=g[L+1][R-1];}
		ask(x,y,x,r[L]);ask(x,y,l[R],y);
		ans=p[ans];
		printf("%d\n",ans);
	}
}
Category: 分块 | Tags:
1
22
2016
0

【bzoj2453】维护队列

2453: 维护队列

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 521  Solved: 222
[Submit][Status][Discuss]

Description

你小时候玩过弹珠吗?
小朋友A有一些弹珠,A喜欢把它们排成队列,从左到右编号为1到N。为了整个队列鲜艳美观,小朋友想知道某一段连续弹珠中,不同颜色的弹珠有多少。当然,A有时候会依据个人喜好,替换队列中某个弹珠的颜色。但是A还没有学过编程,且觉得头脑风暴太浪费脑力了,所以向你来寻求帮助。

Input

输入文件第一行包含两个整数N和M。
第二行N个整数,表示初始队列中弹珠的颜色。
接下来M行,每行的形式为“Q L R”或“R x c”,“Q L R”表示A想知道从队列第L个弹珠到第R个弹珠中,一共有多少不同颜色的弹珠,“R x c”表示A把x位置上的弹珠换成了c颜色。

Output

对于每个Q操作,输出一行表示询问结果。

Sample Input

2 3
1 2
Q 1 2
R 1 2
Q 1 2

Sample Output

2
1

HINT

 

对于100%的数据,有1 ≤ N ≤ 10000, 1 ≤ M ≤ 10000,小朋友A不会修改超过1000次,所有颜色均用1到10^6的整数表示。

Source

我们首先定义一个pre数组,pre[i]表示i之前与i颜色相同的最近的点编号。

那么对于询问(x,y)只要计算x~y的pre中小于x的个数,就是答案。至于计算,我们可以分块来解决。

对于修改,直接暴力修改,能水过的。

代码:

 

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int n,m,len,num,L,R,ans,x,y;
char opt[10];
int a[10010],b[10010],pre[10010],last[1000010],l[110],r[110],flag[110];
int ask(int l,int r)
{
    int ans=0;
    for (int i=l;i<=r;i++) if (b[i]<x) ans++;
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for (int i=1;i<=n;i++)
    {
        pre[i]=b[i]=last[a[i]];
        last[a[i]]=i;
    }
    len=(int)sqrt(n);
    if (n%len==0) num=n/len;else num=n/len+1;
    for (int i=1;i<=num;i++)
    {
        l[i]=(i-1)*len+1;r[i]=min(i*len,n);
        sort(pre+l[i],pre+r[i]+1);
    }
    while (m--)
    {
        scanf("%s%d%d",&opt,&x,&y);
        if (opt[0]=='Q')
        {
            L=x/len;
            if (x>r[L]) L++;
            R=y/len;
            if (y>r[R]) R++;
            if (L==R){printf("%d\n",ask(x,y));continue;}
            ans=ask(x,r[L])+ask(l[R],y);
            for (int i=L+1;i<R;i++)
                ans+=lower_bound(pre+l[i],pre+r[i]+1,x)-pre-l[i];
            printf("%d\n",ans);
        }else
        {
            for (int i=1;i<=n;i++) last[a[i]]=0;
            a[x]=y;
            for (int i=1;i<=num;i++) flag[i]=0;
            for (int i=1;i<=n;i++)
            {
                if (b[i]!=last[a[i]]) flag[(i-1)/len+1]=1;
                b[i]=last[a[i]];
                last[a[i]]=i;
            }
            for (int i=1;i<=num;i++)
            if (flag[i])
            {
                for (int j=l[i];j<=r[i];j++)
                    pre[j]=b[j];
                sort(pre+l[i],pre+r[i]+1);
            }
        }
    }
}
Category: 分块 | Tags:
1
21
2016
0

【bzoj4403】序列统计

4403: 序列统计

Time Limit: 3 Sec  Memory Limit: 128 MB
Submit: 94  Solved: 39
[Submit][Status][Discuss]

Description

给定三个正整数N、L和R,统计长度在1到N之间,元素大小都在L到R之间的单调不降序列的数量。输出答案对10^6+3取模的结果。

Input

输入第一行包含一个整数T,表示数据组数。第2到第T+1行每行包含三个整数N、L和R,N、L和R的意义如题所述。

Output

输出包含T行,每行有一个数字,表示你所求出的答案对106+3取模的结果。

Sample Input

2
1 4 5
2 4 5

Sample Output

2
5

HINT

 

提示

【样例说明】满足条件的2个序列为[4]和[5]。

【数据规模和约定】对于100%的数据,1≤N,L,R≤10^9,1≤T≤100,输入数据保证L≤R。

 

Source

利用可重复的组合,可以推得方案数为C(n,n+r-l+1)-1。然后就用一下lucas定理就可以了。

附:lucas定理:C(m,n)%p=C(m/p,n/p)*C(m%p,n%p)。

代码:

 

#include<cstdio>
#define mod 1000003
using namespace std;
typedef long long ll;
int T;
ll n,L,R,fac[1000010];
ll ksm(ll x,ll y)
{
    ll ans=1;
    while (y)
    {
        if (y%2) ans=ans*x%mod;
        x=x*x%mod;
        y/=2;
    }
    return ans;
}
ll c(ll m,ll n)
{
    if (n<m) return 0;
    ll ans=fac[n];
    ans=ans*ksm(fac[m],mod-2)%mod;
    ans=ans*ksm(fac[n-m],mod-2)%mod;
    return ans;
}
ll lucas(ll m,ll n)
{
    if (!m) return 1;
    return lucas(m/mod,n/mod)*c(m%mod,n%mod);
}
int main()
{
    scanf("%d",&T);
    fac[0]=1;
    for (int i=1;i<=mod;i++) fac[i]=fac[i-1]*i%mod;
    while (T--)
    {
        scanf("%lld%lld%lld",&n,&L,&R);
        printf("%lld\n",(lucas(n,n+R-L+1)+mod-1)%mod);
    }
}
Category: 数学 | Tags:
1
21
2016
0

【bzoj3531】【Sdoi2014】旅行

3531: [Sdoi2014]旅行

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 837  Solved: 426
[Submit][Status][Discuss]

Description

 S国有N个城市,编号从1到N。城市间用N-1条双向道路连接,满足
从一个城市出发可以到达其它所有城市。每个城市信仰不同的宗教,如飞天面条神教、隐形独角兽教、绝地教都是常见的信仰。为了方便,我们用不同的正整数代表各种宗教,  S国的居民常常旅行。旅行时他们总会走最短路,并且为了避免麻烦,只在信仰和他们相同的城市留宿。当然旅程的终点也是信仰与他相同的城市。S国政府为每个城市标定了不同的旅行评级,旅行者们常会记下途中(包括起点和终点)留宿过的城市的评级总和或最大值。
    在S国的历史上常会发生以下几种事件:
”CC x c”:城市x的居民全体改信了c教;
”CW x w”:城市x的评级调整为w;
”QS x y”:一位旅行者从城市x出发,到城市y,并记下了途中留宿过的城市的评级总和;
”QM x y”:一位旅行者从城市x出发,到城市y,并记下了途中留宿过
的城市的评级最大值。
    由于年代久远,旅行者记下的数字已经遗失了,但记录开始之前每座城市的信仰与评级,还有事件记录本身是完好的。请根据这些信息,还原旅行者记下的数字。    为了方便,我们认为事件之间的间隔足够长,以致在任意一次旅行中,所有城市的评级和信仰保持不变。

Input

    输入的第一行包含整数N,Q依次表示城市数和事件数。
    接下来N行,第i+l行两个整数Wi,Ci依次表示记录开始之前,城市i的
评级和信仰。
    接下来N-1行每行两个整数x,y表示一条双向道路。
    接下来Q行,每行一个操作,格式如上所述。

Output

    对每个QS和QM事件,输出一行,表示旅行者记下的数字。

Sample Input

5 6
3 1
2 3
1 2
3 3
5 1
1 2
1 3
3 4
3 5
QS 1 5
CC 3 1
QS 1 5
CW 3 3
QS 1 5
QM 2 4

Sample Output

8
9
11
3

HINT

 

N,Q < =10^5    , C < =10^5


 数据保证对所有QS和QM事件,起点和终点城市的信仰相同;在任意时

刻,城市的评级总是不大于10^4的正整数,且宗教值不大于C。

 

Source

在普通的树链剖分基础上加了一个宗教的限制,但我们发现宗教个数在105范围内,我们可以对每个宗教开一个线段树,然后只要访问到的点开点就可以了,这样就不会超内存了。挺水的

代码:

 

#include<cstdio>
#include<algorithm>
using namespace std;
struct node{int l,r,sum,max;}tr[10000010];
int n,q,edgenum,u,v,tot,cnt,x,y,z,ans;
int f[200010],d[200010],num[200010],son[200010],top[200010],tid[200010],pre[200010],root[200010];
int vet[200010],next[200010],head[200010],w[200010],c[200010];
char opt[20];
void add(int u,int v)
{
    vet[++edgenum]=v;
    next[edgenum]=head[u];
    head[u]=edgenum;
}
void dfs(int u,int fa,int dep)
{
    f[u]=fa;
    d[u]=dep;
    num[u]=1;
    int maxnum=0;
    for (int e=head[u];e;e=next[e])
    {
        int v=vet[e];
        if (v!=fa)
        {
            dfs(v,u,dep+1);
            num[u]+=num[v];
            if (num[v]>maxnum){maxnum=num[v];son[u]=v;}
        }
    }
}
void dfs(int u,int number)
{
    top[u]=number;
    tid[u]=++tot;
    pre[tot]=u;
    if (!son[u]) return;
    dfs(son[u],number);
    for (int e=head[u];e;e=next[e])
    {
        int v=vet[e];
        if (v!=f[u]&&v!=son[u]) dfs(v,v);
    }
}
int lca(int u,int v)
{
    for (;top[u]!=top[v];u=f[top[u]])if (d[top[u]]<d[top[v]]) swap(u,v);
    if (d[u]<d[v]) return u;return v;
}
void change(int l,int r,int &p,int x,int val)
{
    if (!p) p=++cnt;
    if (l==r){tr[p].max=tr[p].sum=val;return;}
    int mid=(l+r)>>1;
    if (x<=mid) change(l,mid,tr[p].l,x,val);else change(mid+1,r,tr[p].r,x,val);
    tr[p].max=max(tr[tr[p].l].max,tr[tr[p].r].max);
    tr[p].sum=tr[tr[p].l].sum+tr[tr[p].r].sum;
}
int askmax(int l,int r,int p,int x,int y)
{
    if (!p) return 0;
    if (l==x&&y==r) return tr[p].max;
    int mid=(l+r)>>1;
    if (y<=mid) return askmax(l,mid,tr[p].l,x,y);else
    if (x>mid) return askmax(mid+1,r,tr[p].r,x,y);else
    return max(askmax(l,mid,tr[p].l,x,mid),askmax(mid+1,r,tr[p].r,mid+1,y));
}
int asksum(int l,int r,int p,int x,int y)
{
    if (!p) return 0;
    if (l==x&&y==r) return tr[p].sum;
    int mid=(l+r)>>1;
    if (y<=mid) return asksum(l,mid,tr[p].l,x,y);else
    if (x>mid) return asksum(mid+1,r,tr[p].r,x,y);else
    return asksum(l,mid,tr[p].l,x,mid)+asksum(mid+1,r,tr[p].r,mid+1,y);
}
int getsum(int u,int v,int c)
{
    int ans=0;
    while (top[u]!=top[v])
    {
        ans+=asksum(1,n,root[c],tid[top[u]],tid[u]);
        u=f[top[u]];
    }
    ans+=asksum(1,n,root[c],tid[v],tid[u]);
    return ans;
}
int getmax(int u,int v,int c)
{
    int ans=0;
    while (top[u]!=top[v])
    {
        ans=max(ans,askmax(1,n,root[c],tid[top[u]],tid[u]));
        u=f[top[u]];
    }
    ans=max(ans,askmax(1,n,root[c],tid[v],tid[u]));
    return ans;
}
int main()
{
    scanf("%d%d",&n,&q);
    for (int i=1;i<=n;i++) scanf("%d%d",&w[i],&c[i]);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    dfs(1,0,1);
    dfs(1,1);
    for (int i=1;i<=n;i++) change(1,n,root[c[i]],tid[i],w[i]);
    while (q--)
    {
        scanf("%s",opt);
        scanf("%d%d",&x,&y);
        if (opt[0]=='C')
        {
            if (opt[1]=='C')
            {
                change(1,n,root[c[x]],tid[x],0);
                c[x]=y;
                change(1,n,root[c[x]],tid[x],w[x]);
            }else {change(1,n,root[c[x]],tid[x],y);w[x]=y;}
        }else
        {
            z=lca(x,y);
            if (opt[1]=='S')
            {
                ans=getsum(x,z,c[x])+getsum(y,z,c[x]);
                if (c[x]==c[z]) ans-=w[z];
                printf("%d\n",ans);
            }else printf("%d\n",max(getmax(x,z,c[x]),getmax(y,z,c[x])));
        }
    }
}
Category: 树链剖分 | Tags:
1
13
2016
0

【bzoj4364】【IOI2014】wall砖墙

4364: [IOI2014]wall砖墙

Time Limit: 30 Sec  Memory Limit: 256 MB
Submit: 77  Solved: 30
[Submit][Status][Discuss]

Description

健佳正在用大小相同的砖块来砌起一面墙。这面墙由 列砖块所组成,它们从左到右的编号0至n-1。各列的高度可以不同。各列的高度就是该列砖块的数量。健佳用如下方式来建造这面墙。最开始每列都没有砖块。此后,健佳通过k个阶段的增加(adding)或移除(removing)砖块操作来砌墙。当所有k个阶段完成后,这面墙就砌好了。在每个阶段中,健佳都会被告知一个连续的砖块列的范围,以及一个高度值h,然后他就完成如下过程:在增加砖块(adding)阶段,对于给定的列范围中高度小于h的列,健佳会增加砖块使它们的高度都恰好等于h。此时他不会改变那些高度大于或等于h的列。在移除砖块(removing)阶段,对于给定的列范围中高度大于 的列,健佳会移除砖块使它们的高度都恰好等于h。此时他不会改变那些高度小于或等于h的列。你的任务就是计算出这面墙的最后形状。

Input


第1行:n, k。

第2+i 行(0≤i≤k-1):op[i], left[i], right[i], height[i]。

n: 这面墙中的列数。

k: 阶段数。

op: 大小为k的数组;op[i]是第i个阶段的类型:1 表示增加阶段(adding) 而 2表示移除阶段(removing) ,其中0≤i≤k-1。

left 和 right: 大小为k的数组;在第i个阶段中,列的范围从第left[i] 列开始到第right[i]列结束(包括两端 left[i] 和 right[i]),其中0≤i≤k-1。这里保证满足left[i]≤right[i]。

height: 大小为k的数组;height[i] 表示在阶段i的高度参数,其中0≤i≤k-1。

Output

共n行,第i行包含一个整数表示finalHeight[i]。

finalHeight: 大小为n的数组;你需要把第i列砖块的最终数量存放到finalHeight[i]中做为返回结果,其中0≤i≤n-1。

Sample Input

输入样例1
10 3
1 3 4 91220
1 5 9 48623
2 3 5 39412

输入样例2

10 6
1 1 8 4
2 4 9 1
2 3 6 5
1 0 5 3
1 2 2 5
2 6 7 0

Sample Output

输出样例1

0
0
0
39412
39412
39412
48623
48623
48623
48623

输出样例2

3
4
5
4
3
3
0
0
1
0

HINT

 



对于100%的数据,1≤n≤2,000,000,1≤k≤500,000。

为什么这道题题面复制过来格式会变成这样。。。所以我们加一条分割线吧。

------------------------------------------------------------------------------------------------------------------------------

用线段树就可以了,直接记录区间的最大值和最小值。不用加tag数组,pushdown的时候,只要用当前父亲的最大最小值去更新儿子的最大最小值就可以了,具体可以看代码。最后加点常数优化就可以了。

代码:

 

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,opt,l,r,x;
struct node{int min,max;}tr[8000010];
void read(int &x)
{
    char ch,fu=0;
    for (ch='*';(ch<'0'||ch>'9')&&ch!='-'; ch=getchar());
    if (ch=='-') fu=1,ch=getchar();
    for (x=0;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    if (fu) x=-x;
}
inline void pushdown(int l,int r,int p)
{
    if (l==r) return;
    int sl=p<<1,sr=p<<1^1;
    if (tr[p].max<tr[sl].min) tr[sl].min=tr[sl].max=tr[p].max;else if (tr[p].max<tr[sl].max) tr[sl].max=tr[p].max;
    if (tr[p].max<tr[sr].min) tr[sr].min=tr[sr].max=tr[p].max;else if (tr[p].max<tr[sr].max) tr[sr].max=tr[p].max;
    if (tr[p].min>tr[sl].max) tr[sl].min=tr[sl].max=tr[p].min;else if (tr[p].min>tr[sl].min) tr[sl].min=tr[p].min;
    if (tr[p].min>tr[sr].max) tr[sr].min=tr[sr].max=tr[p].min;else if (tr[p].min>tr[sr].min) tr[sr].min=tr[p].min;
}
inline void change1(int l,int r,int p,int x,int y,int val)
{
    pushdown(l,r,p);
    if (l==x&&r==y){tr[p].max=max(tr[p].max,val);tr[p].min=max(tr[p].min,val);return;}
    int mid=(l+r)>>1,sl=p<<1,sr=p<<1^1;
    if (y<=mid) change1(l,mid,sl,x,y,val);else
    if (x>mid) change1(mid+1,r,sr,x,y,val);else
    {change1(l,mid,sl,x,mid,val);change1(mid+1,r,sr,mid+1,y,val);}
    tr[p].max=max(tr[sl].max,tr[sr].max);
    tr[p].min=min(tr[sl].min,tr[sr].min);
}
inline void change2(int l,int r,int p,int x,int y,int val)
{
    pushdown(l,r,p);
    if (l==x&&r==y){tr[p].max=min(tr[p].max,val);tr[p].min=min(tr[p].min,val);return;}
    int mid=(l+r)>>1,sl=p<<1,sr=p<<1^1;
    if (y<=mid) change2(l,mid,sl,x,y,val);else
    if (x>mid) change2(mid+1,r,sr,x,y,val);else
    {change2(l,mid,sl,x,mid,val);change2(mid+1,r,sr,mid+1,y,val);}
    tr[p].max=max(tr[sl].max,tr[sr].max);
    tr[p].min=min(tr[sl].min,tr[sr].min);
}
inline void print(int l,int r,int p)
{
    pushdown(l,r,p);
    if (l==r){printf("%d\n",tr[p].max);return;}
    int mid=(l+r)>>1;
    print(l,mid,p<<1);print(mid+1,r,p<<1^1);
}
int main()
{
    read(n);read(m);
    while (m--)
    {
        read(opt);read(l);read(r);read(x);l++;r++;
        if (opt==1) change1(1,n,1,l,r,x);else change2(1,n,1,l,r,x);
    }
    print(1,n,1);
}


 

 

Category: 线段树 | Tags:
1
8
2016
0

【poj3415】Common Substrings

题意:给你两个字符串和k,求不小于k的公共子串的个数。

计算A的某个后缀与B的某个后缀的最长公共前缀L,如果L大于等于k,就加上L-k+1。

我们先把B复制到A后面,并加一个分隔符。

我们考虑同一组大于等于k的height,对于每个B计算之前每个A对其的贡献,对于每个A计算之前每个B对其的贡献。但这样是N2的。所以,我们利用lcp的性质,i~j所有height的最小值,所以我们只要维护一个单调递增的栈即可。

代码:

 

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int K,l,n,top;
ll sum,ans;
int wa[200100],wb[200100],ws[200100],wv[200100],sa[200100],rank[200100],r[200100],height[200100],f[200100],a[200100],b[200100];
char s[200100];
int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}
void da(int *r,int *sa,int n,int m)
{
	int i,j,p,*x=wa,*y=wb,*t;
	for (i=0;i<m;i++) ws[i]=0;
	for (i=0;i<n;i++) ws[x[i]=r[i]]++;
	for (i=1;i<m;i++) ws[i]+=ws[i-1];
	for (i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;
	for (j=1,p=1;p<n;j*=2,m=p)
	{
		p=0;
		for (i=n-j;i<n;i++) y[p++]=i;
		for (i=0;i<n;i++) if (sa[i]>=j) y[p++]=sa[i]-j;
		for (i=0;i<n;i++) wv[i]=x[y[i]];
		for (i=0;i<m;i++) ws[i]=0;
		for (i=0;i<n;i++) ws[wv[i]]++;
		for (i=1;i<m;i++) ws[i]+=ws[i-1];
		for (i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
		for (t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
			x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
	}
}
void calheight(int *r,int *sa,int n)
{
	int i,j,k=0;
	for (i=1;i<=n;i++) rank[sa[i]]=i;
	for (i=0;i<n;height[rank[i++]]=k)
		for (k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
int main()
{
	while (1)
	{
		scanf("%d",&K);
		if (!K) break;
		scanf("%s",s);
		l=strlen(s);
		s[l]=1;;
		scanf("%s",s+l+1);
		n=strlen(s);
		for (int i=0;i<n;i++) r[i]=s[i];
		r[n]=0;
		da(r,sa,n+1,200);
		calheight(r,sa,n);
		for (int i=2;i<=n;i++)
		{
			f[i]=(sa[i]<l);
			height[i]-=K-1;
			if (height[i]<0) height[i]=0;
		}
		height[n+1]=0;
		a[0]=-1;ans=0;
		for (int id=0;id<=1;id++)
		{
			sum=0,top=0;
			for (int i=2;i<=n;i++)
			{
				if (f[i]!=id) ans+=sum;
				a[++top]=height[i+1];
				b[top]=(f[i]==id);
				sum+=(ll)a[top]*b[top];
				while (a[top-1]>=a[top])
				{
					sum-=(ll)(a[top-1]-a[top])*b[top-1];
					a[top-1]=a[top];
					b[top-1]+=b[top];
					top--;
				}
			}
		}
		printf("%lld\n",ans);
	}
}
Category: 后缀数组 | Tags:
1
8
2016
0

【poj1226】Substrings

题意:给你n个串,找到最长长度的字符串,使得其或其的回文串,是每个串的子串,输出长度。

把所有n个串和其回文串都连起来。然后二分答案,对于每个大于等于mid的每组height,只要n个字符串都存在就可行。

代码:

#include<cstdio>
#include<cstring>
using namespace std;
int T,n,len,l;
int wa[100100],wb[100100],ws[100100],wv[100100],sa[100100],r[100100],rank[100100],height[100100],flag[110],id[100100];
char s[1010];
int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}
void da(int *r,int *sa,int n,int m)
{
	int i,j,p,*x=wa,*y=wb,*t;
	for (i=0;i<m;i++) ws[i]=0;
	for (i=0;i<n;i++) ws[x[i]=r[i]]++;
	for (i=1;i<m;i++) ws[i]+=ws[i-1];
	for (i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;
	for (j=1,p=1;p<n;j*=2,m=p)
	{
		p=0;
		for (i=n-j;i<n;i++) y[p++]=i;
		for (i=0;i<n;i++) if (sa[i]>=j) y[p++]=sa[i]-j;
		for (i=0;i<n;i++) wv[i]=x[y[i]];
		for (i=0;i<m;i++) ws[i]=0;
		for (i=0;i<n;i++) ws[wv[i]]++;
		for (i=1;i<m;i++) ws[i]+=ws[i-1];
		for (i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
		for (t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
			x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
	}
}
void calheight(int *r,int *sa,int n)
{
	int i,j,k=0;
	for (i=1;i<=n;i++) rank[sa[i]]=i;
	for (i=0;i<n;height[rank[i++]]=k)
		for (k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
int check(int mid)
{
	int j;
	for (int i=1;i<=len;i=j+1)
	{
		for (;height[i]<mid&&i<=len;i++);
		for (j=i;height[j]>=mid;j++);
		if (j-i+1<n) continue;
		memset(flag,0,sizeof(flag));
		int tot=0;
		for (int k=i-1;k<j;k++)
			if (!flag[id[sa[k]]]){flag[id[sa[k]]]=1;tot++;}
		if (tot>=n) return 1;
	}
	return 0;
}
int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d",&n);
		len=0;
		for (int i=1;i<=n;i++)
		{
			scanf("%s",s);
			l=strlen(s);
			for (int j=0;j<l;j++) {r[len+j]=s[j]+200;id[len+j]=i;}
			r[len+l]=i*2-1;
			id[len+l]=0;
			len+=l+1;
			for (int j=0;j<l;j++) {r[len+j]=s[l-1-j]+200;id[len+j]=i;}
			r[len+l]=i*2;
			id[len+l]=0;
			len+=l+1;
		}
		len--;r[len]=0;
		da(r,sa,len+1,400);
		calheight(r,sa,len);
		height[len+1]=-1;
		int L=0,R=100;
		while (L<R)
		{
			int mid=((L+R)>>1)+1;
			if (check(mid)) L=mid;else R=mid-1;
		}
		if (n==1) printf("%d\n",len/2);else printf("%d\n",R);
	}
}
Category: 后缀数组 | Tags:
1
7
2016
0

【poj3294】Life Forms

题意:给你n个字符串,求最长的字符串使得它是至少n/2+1个字符串的子串,输出所有这样的字符串。

首先我们把n个字符串全部连起来,中间用分隔符连起来。二分答案,然后对于每一组大于等于mid的height,只要判断是否存在于大于n/2个字符串中。

代码:

 

#include<cstdio>
#include<cstring>
using namespace std;
int n,len,l,num;
int wa[100100],wb[100100],ws[100100],wv[100100],sa[100100],r[100100],rank[100100],height[100100],flag[110],id[100100],ans[100100];
char s[1010];
int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];} 
void da(int *r,int *sa,int n,int m)
{
	int i,j,p,*x=wa,*y=wb,*t;
	for (i=0;i<m;i++) ws[i]=0;
	for (i=0;i<n;i++) ws[x[i]=r[i]]++;
	for (i=1;i<m;i++) ws[i]+=ws[i-1];
	for (i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;
	for (j=1,p=1;p<n;j*=2,m=p)
	{
		p=0;
		for (i=n-j;i<n;i++) y[p++]=i;
		for (i=0;i<n;i++) if (sa[i]>=j) y[p++]=sa[i]-j;
		for (i=0;i<n;i++) wv[i]=x[y[i]];
		for (i=0;i<m;i++) ws[i]=0;
		for (i=0;i<n;i++) ws[wv[i]]++;
		for (i=1;i<m;i++) ws[i]+=ws[i-1];
		for (i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];
		for (t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
			x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
	}
}
void calheight(int *r,int *sa,int n)
{
	int i,j,k=0;
	for (i=1;i<=n;i++) rank[sa[i]]=i;
	for (i=0;i<n;height[rank[i++]]=k)
		for (k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
int check(int mid)
{
	int isok=0,j;
	for (int i=1;i<=len;i=j+1)
	{
		for (;height[i]<mid&&i<=len;i++);
		for (j=i;height[j]>=mid;j++)
		if (j-i+1<n/2+1) continue;
		memset(flag,0,sizeof(flag));
		int tot=0;
		for (int k=i-1;k<j;k++)
			if (!flag[id[sa[k]]]){flag[id[sa[k]]]=1;tot++;}
		if (tot>=n/2+1)
		{
			if (!isok){isok=1;num=0;}
			ans[++num]=sa[i];
		}
	}
	return isok;
}
int main()
{
	scanf("%d",&n);
	while (n)
	{
		len=0;num=0;
		for (int i=1;i<=n;i++)
		{
			scanf("%s",s);
			l=strlen(s);
			for (int j=0;j<l;j++) {r[len+j]=s[j]+100;id[len+j]=i;}
			r[len+l]=i;
			id[len+l]=0;
			len+=l+1;
		}
		len--;r[len]=0;
		da(r,sa,len+1,280);
		calheight(r,sa,len);
		height[len+1]=-1;
		int L=0,R=1000;
		while (L<R)
		{
			int mid=((L+R)>>1)+1;
			if (check(mid)) L=mid;else R=mid-1;
		}
		if (!R)printf("?\n");else
		{
			for (int i=1;i<=num;i++)
			{
				int k=ans[i];
				for (int j=0;j<R;j++) printf("%c",r[k+j]-100);
				printf("\n");
			}
		}
		scanf("%d",&n);
		if (n) printf("\n");
	}
}
Category: 后缀数组 | Tags:

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com