牛客网暑期ACM多校训练营(第一场)

题目对我来说太难了,还好做了心理准备-打多校就是为了补题~

AMonotonic Matrix

题目描述 

Count the number of n x m matrices A satisfying the following condition modulo (109+7).
* Ai, j ∈ {0, 1, 2} for all 1 ≤ i ≤ n, 1 ≤ j ≤ m.
* Ai, j ≤ Ai + 1, j for all 1 ≤ i < n, 1 ≤ j ≤ m.
* Ai, j ≤ Ai, j + 1 for all 1 ≤ i ≤ n, 1 ≤ j < m.

输入描述:

The input consists of several test cases and is terminated by end-of-file.
Each test case contains two integers n and m.

输出描述:

For each test case, print an integer which denotes the result.

示例1

输入

复制

1 2
2 2
1000 1000

输出

复制

6
20
540949876

备注:

* 1 ≤ n, m ≤ 103
* The number of test cases does not exceed 105.

这题推了一下n=1的情况,然后感觉有规律,就暴力dfs打了个表,找了找规律。还真的可以找到。。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll c[3005][3005];
ll inv[3005];
ll quick_mod(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1)
        ans=(ans*a)%mod;
        a=(a*a)%mod;
        b/=2;
    }
    return ans;
}
 
void ver()
{
    inv[1] = 1;
    for(int i = 2; i < 3000; i++)
    inv[i] = inv[mod % i] * (mod - mod / i) % mod;
}
void init()
{
    for(int i=1;i<=3000;i++) c[i][i]=1,c[i][0]=1,c[i][1]=i;
    for(int i=2;i<=3000;i++)
    {
        for(int j=1;j<=i;j++)
        {
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
        }
    }
}
int main()
{
    ll n,m;init();ver();
    while(cin>>n>>m)
    {
        //printf("%d %d %d %d %d\n",n+m,n,n+m,n-1,m+n+1);
        ll ans=((c[n+m+1][n+1]*c[n+m+1][n]%mod)%mod)*inv[m+n+1]%mod;
        cout<<ans%mod<<endl;
    }
    return 0;
}
BSymmetric Matrix

链接:https://www.nowcoder.com/acm/contest/139/B
来源:牛客网
 

题目描述

Count the number of n x n matrices A satisfying the following condition modulo m.
* Ai, j ∈ {0, 1, 2} for all 1 ≤ i, j ≤ n.
* Ai, j = Aj, i for all 1 ≤ i, j ≤ n.
* Ai, 1 + Ai, 2 + ... + Ai, n = 2 for all 1 ≤ i ≤ n.
* A1, 1 = A2, 2 = ... = An, n = 0.

输入描述:

The input consists of several test cases and is terminated by end-of-file.
Each test case contains two integers n and m.

输出描述:

For each test case, print an integer which denotes the result.

 

示例1

输入

复制

3 1000000000
100000 1000000000

输出

复制

1
507109376

备注:

* 1 ≤ n ≤ 105
* 1 ≤ m ≤ 109
* The sum of n does not exceed 107.

题意:就是求有多少个满足上面条件的矩阵。

我们可以发现这个矩阵是个邻接矩阵,想到邻接矩阵自然会想到联通图。那么每行总数不超过2,意思就是每个点的度数不超过二,而且可以有重边,那么这个图一定是若干个简单环,且没有自环,因为n的总数不超过1e7,所以可以想到递推。

f[n]代表n个点的答案,我们先求简单的情况,当有我们求第n项时,我们先从前面n-1个点中取出1个点与我们的新点成环,那么就有n-1中选择,而剩下的n-2个点的方案数就是f[n-2],那么答案就是(n-1)*f[n-2],然后可以推广到从前n-1个点取出n-1-k个点与我们当前点成环总共有(n-1-k)!/2种方案(在这里因为有对称的情况所以要除以二),剩下的k个点方案数是f[k]。那么答案就是g(k)=f[k]*(n-1-k)!/2.因为当k=n-2时,即取出1个点与新点成环,我们上面已经求过了,所以求到第k=n-3即可。所以答案就是

f[n]=(n-1)*f[n-2]+sigma(k:0->n-3)C(n-1,k)g(k)。因为式子中有个sigma所以需要化简。过程如下。

得到递推式后就是代码了。注意i要用long long!!!

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll f[100050];
int main()
{
	ll n,mod;
	while(scanf("%d%d",&n,&mod)!=EOF)
	{
		f[0]=1%mod;f[1]=0;f[2]=1%mod;f[3]=1%mod;
		for(long long i=4;i<=n;i++)		//注意i用long long 
		{
			f[i]=(((i-1)*(f[i-1]+f[i-2]))%mod-((i-1)*(i-2)/2*(f[i-3]))%mod+mod)%mod;
		}
		printf("%lld\n",f[n]);
	}
	return 0;
} 

 

DTwo Graphs

链接:https://www.nowcoder.com/acm/contest/139/D
来源:牛客网
 

题目描述

Two undirected simple graphs and where are isomorphic when there exists a bijection on V satisfying  if and only if {x, y} ∈ E2.
Given two graphs and , count the number of graphs satisfying the following condition:
* .
* G1 and G are isomorphic.

输入描述:

The input consists of several test cases and is terminated by end-of-file.
The first line of each test case contains three integers n, m1 and m2 where |E1| = m1 and |E2| = m2.
The i-th of the following m1 lines contains 2 integers ai and bi which denote {ai, bi} ∈ E1.
The i-th of the last m2 lines contains 2 integers ai and bi which denote {ai, bi} ∈ E2.

输出描述:

For each test case, print an integer which denotes the result.

 

示例1

输入

复制

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

输出

复制

2
3

备注:

* 1 ≤ n ≤ 8
* 
* 1 ≤ ai, bi ≤ n
* The number of test cases does not exceed 50.

这题比赛时没想到正解,真的该好好反思一下自己了。队友说找用点的度数找规律,然后我就无从下手了。。

看到n=8的时候可以想到这题时间复杂度应该是n!或者2^n这种级别。

自然会想到直接枚举所有映射,因为自己到自己可能存在a2次映射,那么我们算图1和图2的映射总次数a1,答案中肯定重复了a2次,那么a1/a2就是我们求的答案。

#include <bits/stdc++.h>
using namespace std;
int ma1[15][15],ma2[15][15];
int a[15];
int main()
{
	int n,m1,m2;
	while(cin>>n>>m1>>m2)
	{
		memset(ma1,0,sizeof(ma1));
		memset(ma2,0,sizeof(ma2));
		for(int i=1;i<=m1;i++)
		{
			int x,y;cin>>x>>y;
			ma1[x][y]=1;
			ma1[y][x]=1;
		}
		for(int i=1;i<=m2;i++)
		{
			int x,y;cin>>x>>y;
			ma2[x][y]=1;
			ma2[y][x]=1;
		}
		for(int i=1;i<=n;i++)
		a[i]=i;
		int a1=0,a2=0;
		do{
			int flag=1;
			for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
			if(ma1[i][j]&&(!ma2[a[i]][a[j]])){flag=0;break;
			}
			a1+=flag;
		}while(next_permutation(a+1,a+1+n));
		for(int i=1;i<=n;i++)
		a[i]=i;
		do{
			int flag=1;
			for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
			if(ma1[i][j]&&(!ma1[a[i]][a[j]])){
				flag=0;break;
			}
			a2+=flag;
		}while(next_permutation(a+1,a+1+n));
		cout<<a1/a2<<endl;
	}
	return 0;
}
ERemoval

链接:https://www.nowcoder.com/acm/contest/139/E
来源:牛客网
 

题目描述

Bobo has a sequence of integers s1, s2, ..., sn where 1 ≤ si ≤ k.
Find out the number of distinct sequences modulo (109+7) after removing exactly m elements.

输入描述:

The input consists of several test cases and is terminated by end-of-file.
The first line of each test case contains three integers n, m and k.
The second line contains n integers s1, s2, ..., sn.

输出描述:

For each test case, print an integer which denotes the result.

 

示例1

输入

复制

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

输出

复制

2
4

备注:

* 1 ≤ n ≤ 105
* 1 ≤ m ≤ min{n - 1, 10}
* 1 ≤ k ≤ 10
* 1 ≤ si ≤ k
* The sum of n does not exceed 106.

题意:长度为n的字符串,删掉m个字符后有多少种不同的字串。

思路:看到这题应该可以想到dp。n的范围1e5,m只有10,那么我们可以用dp[i][j]表示前i个字符构成的子串删掉j个字符后有多少种不同的串。

我们可以推出方程:dp[i][j]=dp[i-1][i-1]+dp[i-1][j-1].但是如果前面有与a[i]相同的字符a[k] (k<i),并且i与k的位置距离小于等于j,那么就会产生重复。

举个例子cdeaaaemkl

当我们i=7,j=4时,a[3]=a[7],那么如果删掉中间的[eaaa]字串就会变成[cde] (注意i=7,所以不是cdemkl),因为我们已经在前面

i=3时算过了一次[cde]这种情况,所以我们需要dp[7][4]-dp[2][0](仔细想想为什么要减去dp[2][0]而不是dp[3][0]).

下面给出代码,不加读入优化会T,我也不造为什么。

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
const ll mod=1e9+7;
ll dp[100005][15];
int pre[100005];
int now[100005];
int a[100005];

int main()
{
	int n,m,k;
	while(scanf("%d%d%d",&n,&m,&k)!=EOF)
	{
		memset(dp,0,sizeof(dp));
		memset(pre,0,sizeof(pre));
		memset(now,0,sizeof(now));
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			pre[i]=now[a[i]];
			now[a[i]]=i;
		}
		for(int i=0;i<=m;i++) dp[i][i]=1;
		
		for(int i=1;i<=n;i++)
		{
			dp[i][0]=1;
            int d = i - pre[i];
			for(int j=1;j<=m&&j<i;j++)
			{
				dp[i][j]=((dp[i-1][j]+dp[i-1][j-1])%mod+mod)%mod;
				if(pre[i]!=0&&d<=j)
				{
					dp[i][j]=((dp[i][j]-dp[pre[i]-1][j-d])%mod+mod)%mod;
				}
			}
		}
		printf("%lld\n",dp[n][m]);
	}
	return 0;
}
FSum of Maximum

链接:https://www.nowcoder.com/acm/contest/139/F
来源:牛客网
 

题目描述

Given a1, a2, ..., an, find 

modulo (109+7).

输入描述:

The input consists of several test cases and is terminated by end-of-file.
The first line of each test case contains an integer n.
The second line contains n integers a1, a2, ..., an.

输出描述:

For each test case, print an integer which denotes the result.

 

示例1

输入

复制

2
1 2
5
2 3 3 3 3
​

输出

复制

3
453

备注:

* 1 ≤ n ≤ 1000
* 1 ≤ ai ≤ 109
* The number of test cases does not exceed 10.

思路:我们可以看出a的顺序与答案无关,我们先排个序,然后我们可以求最大值x属于[a[i-1]+,a[i]]时对答案的贡献。

先考虑a[1]到a[i-1]的方案数。

因为a[i]>a[i-1],x=a[i-1]+1>a[i],那么前i-1个数可以任意取。贡献就是now= \prod_{j=1}^{i-1} a[j] 。

考虑a[i]到a[n]的方案数。

可以想到a[i]到a[n]至少有一个x,且取值不能超过x。那么根据容斥原理。我们可以求出有一个x的情况减去2个x的情况加上3个x的情况。。。方案数就是\sum_{j=1}^{n-i+1} \bigl(\begin{smallmatrix} j\\ n-i+1 \end{smallmatrix}\bigr) *(-1)^{j-1}*x^{n-i+1-j}明显这个时一个二项式展开的一部分,把它化简后我们记为f(x).

那么x对答案的贡献就是now*f(x)*x。

所以对于a[i]>a[i-1]。我们需要求\sum_{ai}^{x=a[i-1]+1}*now*f(x)*x

化简下就是now*\sum_{x=a[i-1]+1}^{a[i]}*x(*x^{n-i+1}-(x-1)^{n-i+1}),我们求出他的前k+2项对他做拉格朗日插值即可。

#include <bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:1024000000,1024000000")
#define mem(a,b) memset((a),(b),sizeof(a))
#define MP make_pair
#define pb push_back
#define fi first
#define se second
#define sz(x) (int)x.size()
#define all(x) x.begin(),x.end()
#define _GLIBCXX_PERMIT_BACKWARD_HASH
#include <ext/hash_map>
using namespace __gnu_cxx;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef vector<int> VI;
typedef vector<ll> VL;
struct str_hash
{
    size_t operator()(const string& str)const
    {
        return __stl_hash_string(str.c_str());
    }
};
const int INF=0x3f3f3f3f;
const ll LLINF=0x3f3f3f3f3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-4;
const int MAX=2e5+10;
const ll mod=1e9+7;
/**************************************** ?head ?****************************************/
namespace polysum
{
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
    const int D=2010;
    ll a[D],f[D],g[D],p[D],p1[D],p2[D],b[D],h[D][2],C[D];
    ll powmod(ll a,ll b)
    {
        ll res=1;
        a%=mod;
        assert(b>=0);
        for(; b; b>>=1)
        {
            if(b&1)res=res*a%mod;
            a=a*a%mod;
        }
        return res;
    }
    ll calcn(int d,ll *a,ll n)   // a[0].. a[d] ?a[n]?
    {
        if (n<=d) return a[n];
        p1[0]=p2[0]=1;
        rep(i,0,d+1)
        {
            ll t=(n-i+mod)%mod;
            p1[i+1]=p1[i]*t%mod;
        }
        rep(i,0,d+1)
        {
            ll t=(n-d+i+mod)%mod;
            p2[i+1]=p2[i]*t%mod;
        }
        ll ans=0;
        rep(i,0,d+1)
        {
            ll t=g[i]*g[d-i]%mod*p1[i]%mod*p2[d-i]%mod*a[i]%mod;
            if ((d-i)&1) ans=(ans-t+mod)%mod;
            else ans=(ans+t)%mod;
        }
        return ans;
    }
    void init(int M)
    {
        f[0]=f[1]=g[0]=g[1]=1;
        rep(i,2,M+5) f[i]=f[i-1]*i%mod;
        g[M+4]=powmod(f[M+4],mod-2);
        per(i,1,M+4) g[i]=g[i+1]*(i+1)%mod;
    }
    ll polysum(ll m,ll *a,ll n)   // a[0].. a[m] \sum_{i=0}^{n-1} a[i]
    {
        ll b[D];
        for(int i=0; i<=m; i++) b[i]=a[i];
        b[m+1]=calcn(m,b,m+1);
        rep(i,1,m+2) b[i]=(b[i-1]+b[i])%mod;
        return calcn(m+1,b,n-1);
    }
    ll qpolysum(ll R,ll n,ll *a,ll m)   // a[0].. a[m] \sum_{i=0}^{n-1} a[i]*R^i
    {
        if (R==1) return polysum(n,a,m);
        a[m+1]=calcn(m,a,m+1);
        ll r=powmod(R,mod-2),p3=0,p4=0,c,ans;
        h[0][0]=0;
        h[0][1]=1;
        rep(i,1,m+2)
        {
            h[i][0]=(h[i-1][0]+a[i-1])*r%mod;
            h[i][1]=h[i-1][1]*r%mod;
        }
        rep(i,0,m+2)
        {
            ll t=g[i]*g[m+1-i]%mod;
            if (i&1) p3=((p3-h[i][0]*t)%mod+mod)%mod,p4=((p4-h[i][1]*t)%mod+mod)%mod;
            else p3=(p3+h[i][0]*t)%mod,p4=(p4+h[i][1]*t)%mod;
        }
        c=powmod(p4,mod-2)*(mod-p3)%mod;
        rep(i,0,m+2) h[i][0]=(h[i][0]+h[i][1]*c)%mod;
        rep(i,0,m+2) C[i]=h[i][0];
        ans=(calcn(m,C,n)*powmod(R,n)-c)%mod;
        if (ans<0) ans+=mod;
        return ans;
    }
} // polysum::init();
ll pow2(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=(ans*a)%mod;
        a=(a*a)%mod;
        b/=2;
    }
    return ans%mod;
}
ll a[1005];
ll b[1005];
int main()
{
    ll n;
    polysum::init(1005);
    while(cin>>n)
    {
        for(int i=1; i<=n; i++)
        cin>>a[i];
        ll now=1;
        sort(a+1,a+n+1);
        ll ans=0;
        for(int i=1; i<=n; i++)
        {
            if(a[i]==a[i-1]) 
			{
				now=(now*a[i])%mod;
				continue;
        	}
        	b[0]=0;
            for(int j=1; j<=n-i+2; j++)
            {
                b[j]=(j*(pow2(j,n-i+1)-pow2(j-1,n-i+1)+mod)%mod)%mod;
                //cout<<b[j]<<endl;
            }
            ll tmp=((polysum::polysum(n-i+2,b,a[i]+1)-polysum::polysum(n-i+2,b,a[i-1]+1))%mod+mod)%mod;
            ans=(ans+tmp*now)%mod;
			now=(now*(a[i]))%mod;
        }
        printf("%lld\n",ans%mod);
    }
    return 0;
}

 

ISubstring

链接:https://www.nowcoder.com/acm/contest/139/I
来源:牛客网
 

题目描述

Two strings u1 u2 ... uk and v1 v2 ... vl are isomorphic if and only if k = l and there exists a injection g such that ui = g(vi) for all i ∈ {1, 2, ..., k}.
Note that a function f is injection if and only if f(x) ≠ f(y) for all x ≠ y.
Bobo would like to choose some strings from all n(n +1)/2 substrings of the given string s1 s2 ... sn.
Find out the maximum number of strings he may choose so that no two chosen strings are isomorphic.

输入描述:

The input consists of several test cases and is terminated by end-of-file.
The first line of each test case contains an integer n.
The second line contains a string s1, s2, ..., sn.

输出描述:

For each test case, print an integer which denotes the result.

 

示例1

输入

复制

4
abaa
4
abab

输出

复制

6
4

备注:

* 1 ≤ n ≤ 5x 104
* si ∈ {a, b, c}
* The sum of n does not exceed 2 x 105.

枚举一下3!种同构情况,然后拼成一个字符串后缀数组求一下不同的子串个数,发现aa,bb,c相同字母的串被重复计算3次,其他串被重复计算6次,所以答案加上(3*(计算三次的)+(计算六次的))/6即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 310000, M = 310000;
char s[N];
int sa[N], t[N], t2[N], c[M], n;
int ran[N], high[N];

bool cmp(int *y,int i,int k)
{
	return y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k];
}
void build(int m)
{
	int i,*x=t,*y=t2;
	for(int i=0;i<m;i++) c[i]=0;
	for(int i=0;i<n;i++) c[x[i]=s[i]]++;
	for(int i=1;i<m;i++) c[i]+=c[i-1];
	for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
	
	for(int k=1,p;k<=n;k<<=1,m=p)
	{
		p=0;
		for(int i=n-k;i<n;i++) y[p++]=i;
		for(int i=0;i<n;i++) if(sa[i]>=k)
		{
			y[p++]=sa[i]-k;
		}
		for(int i=0;i<m;i++)c[i]=0;
		for(int i=0;i<n;i++) c[x[y[i]]]++;
		for(int i=1;i<m;i++) c[i]+=c[i-1];
		for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
		swap(x,y);
		p=1;
		x[sa[0]]=0;
		for(int i=1;i<n;i++)
		{
			x[sa[i]]=cmp(y,i,k)?p-1:p++;
		}
		if(p>=n)break;
	}
}
void get_high()
{
	int k=0;
	for(int i=0;i<n;i++) ran[sa[i]]=i;
	for(int i=0;i<n;i++)
	{
		if(k) k--;
		int j=sa[ran[i]-1];
		while(s[i+k]==s[j+k]) k++;
		high[ran[i]]=k;
	}
}
char s1[N];
int a[6];
char f[6];
int main()
{
	while(scanf("%d",&n)!=EOF)
	{
		scanf("%s",s);
		int l=strlen(s);
		a[0]=0;a[1]=1;a[2]=2;
		
		while(next_permutation(a,a+3))
		{
			for(int i=0;i<3;i++)
			{
				f[i]='a'+a[i];
			}
			s[n++]='a'-1;
			for(int i=0;i<l;i++)
			{
				s[n++]=f[s[i]-'a'];
			}
		}
		
	    n = strlen(s)+1;
		//cout<<s<<endl;
	    int maxi = 0;
	    for(int i = 0; i < n; i++)
	    {
	        maxi = maxi > s[i] ? maxi : s[i];
	    }
	    ll ans=0;
	    s[n-1] = 0;
	    build(maxi+1);
	    get_high();

        int last=0;
        for(int i=6;i<n;i++){
            int tmp=l-sa[i]%(l+1);
            int tadd=tmp-min(high[i],min(last,tmp));
            ans+=tadd;
            last=tmp;
        }
		
		
		ll flag=1;
 		ll mx=0;
        for(int i=0;i<l;i++){
            if(i>0&&s[i]!=s[i-1]){
                mx=max(mx,flag);
                flag=1;
            }else
                flag++;
        }
        mx=max(mx,flag);
		cout<<(ans+mx*3)/6<<endl;
	}
    return 0;
}

 

J

Different Integers

链接:https://www.nowcoder.com/acm/contest/139/J
来源:牛客网
 

题目描述

Given a sequence of integers a1, a2, ..., an and q pairs of integers (l1, r1), (l2, r2), ..., (lq, rq), find count(l1, r1), count(l2, r2), ..., count(lq, rq) where count(i, j) is the number of different integers among a1, a2, ..., ai, aj, aj + 1, ..., an.

输入描述:

The input consists of several test cases and is terminated by end-of-file.
The first line of each test cases contains two integers n and q.
The second line contains n integers a1, a2, ..., an.
The i-th of the following q lines contains two integers li and ri.

输出描述:

For each test case, print q integers which denote the result.

 

示例1

输入

复制

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

输出

复制

2
1
3

备注:

* 1 ≤ n, q ≤ 105
* 1 ≤ ai ≤ n
* 1 ≤ li, ri ≤ n
* The number of test cases does not exceed 10

今天上午刚学了莫队分块,下午看到这题,不敢写,队友在网上找了个离线的树状数组的板子,然后把原数组倍增一边,就变成求连续区间不同数的个数这类经典题目了。

这题官方正解是离线按照r 从小到大处理询问,考虑没出现的数字个数
假设r = last[x],那么当l < first[x] 时,x 没出现
用树状数组维护即可。

先给个官方代码。

#include <bits/stdc++.h>
struct Query
{
    int l, r, id;
};
bool operator < (const Query& u, const Query& v)
{
    return u.r < v.r;
}
int main()
{
    int n, q;
    while (scanf("%d%d", &n, &q) == 2) {
        std::vector<int> a(n), first(n, -1), last(n);
        int total = 0;
        for (int i = 0; i < n; ++ i) {
            scanf("%d", &a[i]);
            a[i] --, last[a[i]] = i;
            if (first[a[i]] == -1) {
                total ++, first[a[i]] = i;
            }
        }
        std::vector<Query> queries;
        for (int i = 0, l, r; i < q; ++ i) {
            scanf("%d%d", &l, &r);
            queries.push_back(Query {l - 1, r - 1, i});
        }
        std::sort(queries.begin(), queries.end());
        std::vector<int> count(n), result(q);
        for (int i = 0, k = 0; i < n; ++ i) {
            while (k < q && queries[k].r == i) {
                int& ref = result[queries[k].id] = total;
                for (int j = queries[k].l; j < n; j += ~j & j + 1) {
                    ref -= count[j];
                }
                k ++;
            }
            if (last[a[i]] == i) {
                for (int j = first[a[i]] - 1; ~j; j -= ~j & j + 1) {
                    count[j] ++;
                }
            }
        }
        for (int i = 0; i < q; ++ i) {
            printf("%d\n", result[i]);
        }
    }
}

树状数组的常规写法

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int max_n = 1e5 + 1;
const int max_q = 1e5 + 1;
int arr[max_n];
int last[max_n];
int first[max_n];
int tree[max_n];
int ans[max_q];
struct Nod
{
    int r,l,ind;
    bool operator<(const Nod& obj)const
    {
        return r < obj.r;
    }
};
Nod query[max_q];
int lowbit(int x)
{
    return x & -x;
}
int getSum(int x)
{
    int ans = 0 ;
    while(x>0)
    {
        ans += tree[x];
        x -= lowbit(x);
    }
    return ans;
}
void add(int x,int v,int len)
{
    while(x < len)
    {
        tree[x] += v;
        x += lowbit(x);
    }
}
int main()
{
    int n,q;
    while(scanf("%d%d",&n,&q) == 2)
    {

        memset(tree,0,sizeof(tree));
        memset(first,-1,sizeof(first));

        int total = 0 ;
        for(int i = 1 ; i<= n ; ++i)
        {
            scanf("%d",arr+i);
            if(first[arr[i]] == -1)
            {
                total++;
                first[arr[i]] = i;
            }
            last[arr[i]] = i;
        }
        for(int i = 0 ; i < q ; ++i)
        {
            scanf("%d%d",&query[i].l,&query[i].r);
            query[i].ind = i ;
            query[i].r--;
        }
        sort(query,query+q);
        int ind = 1;
        for(int i = 0 ; i < q ; i++)
        {
            while(ind <= query[i].r)
            {
                if(last[arr[ind]] == ind)
                {
                    add(first[arr[ind]],1,n+1);
                }
                ind++;
            }
            ans[query[i].ind] = total - (getSum(query[i].r) - getSum(query[i].l));
        }
        for(int x = 0 ; x < q ; ++x)
        {
            printf("%d\n",ans[x]);
        }
    }
    return 0;
}

下面是莫队分块的代码,这题莫队比较容易超时,需要优化,对于查询区间小于等于1的我们要提前算出来。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int b[maxn],block[maxn],a[maxn],cnt[maxn];
void read(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int n,m,ans;
struct node{
	int l,r,pos;
}q[100005];

bool cmp(node &q,node &w)
{
    return block[q.l]!=block[w.l]?block[q.l]<block[w.l]:q.r<w.r;
}

void inc(int x)
{
	//printf("%d-%d---\n",x,cnt[x]);
	if(cnt[x]==0) ans++;
	cnt[x]++;
}
void del(int x)
{
	cnt[x]--;
	if(cnt[x]==0) ans--;
}
int main()
{
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		ans=0;
		int val=sqrt(n+1);
		memset(cnt,0,sizeof(cnt));
		for(int i=1;i<=n;i++)
		{
			read(a[i]);block[i]=(i-1)/val+1;
			inc(a[i]);
		}
		int qcnt=0;
		for(int i=1;i<=m;i++)
		{
			int x,y;read(x);read(y);
			if(y-x<=1) {b[i]=ans;continue;}
			qcnt++;
			q[qcnt].pos=i;
			q[qcnt].l=x;
			q[qcnt].r=y;
		}
		sort(q+1,q+1+qcnt,cmp);
        int L=1,R=L-1;
        inc(a[L]);
		for(int i=1;i<=qcnt;i++)
		{
			while(R>q[i].r)
			inc(a[--R]);
			while(L<q[i].l)
			inc(a[++L]);
			while(L>q[i].l)
			del(a[L--]);
			while(R<q[i].r)
			del(a[R++]);
			b[q[i].pos]=ans;
		}
		for(int i=1;i<=m;i++)
		printf("%d\n",b[i]);
	}
    return 0;
}
/*
12 7
1 2 2 3 2 7 8 5 4 7 8 9
1 3
2 4
4 5
7 8
4 7
1 6
1 7

8
8
8
8
8
6
6
*/

 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值