2021暑假集训Day 4

数论 背包 分块 数论?

A

Decription:

若数列A存在位置k,满足
①i<k,ai<ai+1
②i>=k,ai>ai+1
则称该数列为单峰数列
给出正整数n,求全排列n中存在多少个单峰数列

Input

给出1个正整数n

Output

单峰数列个数(对10^9+7取模)

Sample Input:

3

Sample Output:

4

Data Constraint

20%:n ≤ \leq 10
50%:n ≤ \leq 10^5
100%:2 ≤ \leq n ≤ \leq 10^18

思路:
数据这么大,明显是数论,是的,尝试着就来推一波,对于最大值n来说,他肯定就是那个峰值,那么我们试着看一下,峰值肯定是n,考虑n左右放哪些数,决定放哪些数以后,排列是固定的即n左边放的数单调递增,n右边放的数单调递减方案数为C(n-1,0)+C(n-1,1)+…+C(n-1,n-1)= 2 ( n − 1 ) 2^{(n-1)} 2(n1)
那么不就是一道水体了吗,快速幂取模走起。

#include<iostream>
#include<cstdio>
#define ll long long
#define mod 1000000007
using namespace std;
ll n;
ll ksm(ll x,ll y)
{
	ll res=1;
    while(y)
	{
	  if(y&1) res=(res%mod*x%mod)%mod;
	  x=(ll)(x*x)%mod,y>>=1; 
	}
	return res;	
}
int main()
{
    scanf("%lld",&n);
    n--;
    printf("%lld",ksm(2,n));
	return 0;
} 

B

Decription:

XJQ要做N件事,需要消耗它不同的精力,做完之后还会获得一定的信誉。对于每件事他可以选择做或不做,而且他还有可能真香一些事,真香之后这些事就不能再做了。对于每次真香,他都想知道以他的精力,最多可以获得多少信誉。

Input

第一行2个正整数n,m表示有n件事它开始拥有的精力
第n行,每行两个数,表示需要精力和可以获得的信誉
接下来n-1行一个数表示了他真香了哪件事
每次真香对下一次真香有影响

Output

第一行为开始时可以获得的最多信誉
接下来n-1行表示他真香了某件事后可以获得的最多信誉

Sample Input:

5 20
7 1
1 1
13 1
1 9
1 16
1
4
2
3

Sample Output:

27
27
18
17
16

Data Constraint

1~3:n,m ≤ \leq 100
3~5:n ≤ \leq 1000,m ≤ \leq 5000
6~10:n ≤ \leq 4000,m ≤ \leq 10000
其他数均为正整数,且不大于10000

思路:
看得出来是个01背包,倒过来dp不就完事了?秒了吧?

#include<iostream>
#include<cstdio>
#define M 10000
#define N 4000 
using namespace std;
int n,m,dp[M+11],f[N+11],val[N+11],ban[N+11],maxn,ans[N+11];
bool bo[N+11];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d%d",&f[i],&val[i]);
    for(int i=1;i<=n-1;i++) scanf("%d",&ban[i]),bo[ban[i]]=1;
    for(int i=1;i<=n;i++) 
    {
    	if(!bo[i])
    	{
    		ban[n]=i;
    		break;
		}
	}
    for(int i=n;i>=1;i--)
    {
    	maxn=0;
    	for(int j=m;j>=f[ban[i]];j--)
    		dp[j]=max(dp[j-f[ban[i]]]+val[ban[i]],dp[j]),maxn=max(dp[j],maxn);
    	ans[i]=maxn;
	}
	for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
	return 0; 
} 

C

Decription:

给定一个长度为n的数列a,有m次操作,操作分两种:
1 给出[l,r],表示将此区间的数轮转一次,即变成 a r a_r ar, a l a_l al a r − 1 a_{r-1} ar1
2 给出[l,r]和k,询问此区间内 a i a_i ai==k的个数

Input

第一行给出n,m
第二行给出序列a
接下来m行给出m次操作

Output

对于每个询问,给出一行表示答案

Sample Input:

7 6
1 2 2 3 2 1 3
2 3 6 2
1 1 6
2 2 4 1
1 3 6
2 6 7 3
2 3 5 2

Sample Output:

2
1
2
3

Data Constraint

1~3:n,m ≤ \leq 100
3~5:n ≤ \leq 1000,m ≤ \leq 5000
6~10:n ≤ \leq 4000,m ≤ \leq 10000
其他数均为正整数,且不大于10000

思路:
分块,我的分块写炸了,总的复杂度为O(n n \sqrt{n} n )
转载了disPlayLzy_的博客代码,大家先看一看,小编会后续更改的,再次感谢disPlayLzy_。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
#define N 100005

using namespace std;

int A[335][N], Belong[N], C[355], D[355], num, cnt, n, m;

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 = x * f;
}

deque <int> Q[N];
 
int main()
{
//	freopen("queue.in", "r", stdin);
//	freopen("queue.out", "w", stdout);
	read(n); read(m);
	num = sqrt(n);
	for (int i = 1; i <= n; i++) 
	{
		 int x; read(x); 
		 if (i  > num * cnt) ++cnt;
		 Q[cnt].push_back(x);
		 Belong[i] = cnt, ++A[cnt][x];
	}
	while (m--)
	{
		int opt; read(opt);
		if (opt == 1)
		 {
			int l, r; read(l); read(r);
			if (Belong[l] == Belong[r])
			{
				int G = Belong[l];
				l = l % num; if (l == 0) l = num;
				r = r % num; if (r == 0) r = num;
			    for (int i = 1; i < l; i++) //把 1~L 取出来 
				    C[i] = Q[G].front(), Q[G].pop_front();
			    for (int i = 1; i <= num - r; i++)
			        D[i] = Q[G].back(), Q[G].pop_back();
			        
			    int x = Q[G].back(); Q[G].pop_back(); Q[G].push_front(x); 
				for (int i = l - 1; i >= 1; i--) Q[G].push_front(C[i]);
			    for (int i = 1; i <= num - r; i++) Q[G].push_back(D[(num - r) - i + 1]);
			}
			else 
			{
				int G = Belong[l], k = l; while (Belong[k + 1] == G) ++k;
			    int len1 = k - l + 1;
			    for (int i = 1; i <= len1; i++)
				    C[len1 - i + 1] = Q[G].back(), Q[G].pop_back(), A[G][C[len1 - i + 1]]--; 
				    
			    int F = Belong[r], h = r; while (Belong[h - 1] == F) --h;
			    int len2 = r - h + 1; 
			    for (int i = 1; i <= len2; i++)
			        D[i] = Q[F].front(), Q[F].pop_front(), A[F][D[i]]--;
			    
			    Q[G].push_back(D[len2]), A[G][D[len2]]++;
			    for (int i = 1; i < len1; i++) Q[G].push_back(C[i]), A[G][C[i]]++;
	            
	            for (int i = len2 - 1; i >= 1; i--) Q[F].push_front(D[i]), A[F][D[i]]++; 
				int x = Q[F - 1].back(); if (F == G + 1) x = C[len1];
				Q[F].push_front(x), A[F][x]++;
	            
	            int last = C[len1];
	            for (int i = G + 1; i <= F - 1; i++)
	            {
				    Q[i].push_front(last); A[i][last]++;
					last = Q[i].back(); Q[i].pop_back();
					A[i][last]--; 	    
			    }
			}
		} 
		else 
		{
			int l, r, K, orz = 0; read(l); read(r); read(K);
			if (Belong[l] == Belong[r])
			{
				int G = Belong[l];
				l = l % num; if (l == 0) l = num;
				r = r % num; if (r == 0) r = num;
			    for (int i = 1; i < l; i++) 
				    C[i] = Q[G].front(), Q[G].pop_front();
			    for (int i = 1; i <= num - r; i++)
			        D[i] = Q[G].back(), Q[G].pop_back();
			        
				for (int i = l - 1; i >= 1; i--) 
				{
					if (C[i] == K) ++orz;
				    Q[G].push_front(C[i]);
				}
			    for (int i = 1; i <= num - r; i++) 
				{
				    if (D[(num - r) - i + 1] == K) ++orz;
					Q[G].push_back(D[(num - r) - i + 1]);
			    }
			    orz = A[G][K] - orz;
			}
			else 
			{
				int G = Belong[l], k = l; while (Belong[k + 1] == G) ++k;
			    int len1 = k - l + 1;
			    for (int i = 1; i <= len1; i++) C[len1 - i + 1] = Q[G].back(), Q[G].pop_back(); 
				for (int i = 1; i <= len1; i++) 
				{
				    if (C[i] == K) ++orz;
					Q[G].push_back(C[i]);
				}
				
			    int F = Belong[r], h = r; while (Belong[h - 1] == F) --h;
			    int len2 = r - h + 1; 
			    for (int i = 1; i <= len2; i++) D[i] = Q[F].front(), Q[F].pop_front();  
	            for (int i = len2; i >= 1; i--) 
				{
				    if (D[i] == K) ++orz;
					Q[F].push_front(D[i]); 
			    }
	            for (int i = G + 1; i <= F - 1; i++) orz += A[i][K];
			} 
			printf("%d\n", orz);
		}
	}
	return 0;
}

D 后续更新

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值