20201103提高组训练(选数字&堆箱子&快速排序&统计学带师)

A 选数字

题目

题目描述

桌面上有N个数字排成一排,小武要求小林从中选出3个数字,使得这3个数字的按位或的结果恰好等于x,小林很快 就解决了这个问题,小武想了想,决定把问题加强一下,小武会问小林Q次问题,每次选的3个数字只能在从左往右 的第l个数和第r个数之间选择,并且要小林说出符合要求的方案数,小林顿时不会了,于是把问题交给了你。

输入格式

第一行两个数N和Q 第二行N个数,按照从左到右的顺序给出桌面上的数字 接下来Q行,每行3个数字,分别为l,r,x

输出格式

Q行,每行一个数表示方案数

输入输出样例

输入 #1

10 5
2 4 3 7 6 9 8 7 10 15
1 5 7
2 8 14
3 5 7
1 10 15
6 9 12

输出 #1

9
1
1
81
0

说明/提示

数据范围

对于20%的数据,n,Q≤100
对于60%的数据,n,Q≤10000
对于100%的数据,1≤l≤r≤n≤10^5 ,1≤Q≤10^5,1≤桌面上每个数字,x≤255

样例解释

第一个询问,选择范围为{2,4,3,7,6}。除了选2 4 6不行,剩下9种方案均可以

思路

首先,根据或运算的性质,一个数a[i],若它在二进制下的某一位为1,而x在对应位上为0,a[i]是绝对不可能通过或运算得到x的

考虑到x和a[]只能去到255,我们开数组sum[i] [j]表示1~i中a[k]的个数,满足(a[k] & j)==a[k],即a[k]有可能通过或运算得到 j

定义f(x)表示x在二进制下有多少个"1"(程序中cnt[x]的意义同f(x))

则对于每一组问题l,r,x

[ans=Sigma _{∀j,(j&x)==j}{(-1)^{f(x)-f(j)}cdot C^3_{sum[r][j] - sum[l-1][j]}} ]

即在[l,r]中,对于所有j,x & j == j,所有(a[i]&j)==a[i] (有可能异或得到x的a[i]),若f(x)-f(j)为奇数,从ans中减去 满足条件的a[i]的数量 的组合数,否则加上。

为什么呢?

在所有可能组成x的数中取三个,有可能取到的数不合法(或运算后不等于x),就要减去,所以我们要枚举j,如果j的二进制下只有一位和x不同(j的那一位为0,而x为1),那么区间内经过或运算可能得到j的数之间 进行或运算是一定得不到x的,这些数的组合应当减去,故容斥系数为-1。但是有可能会减掉一些重复的组合,如(0000,1000,1001)在j=(1011)和j=(1101)时均会被减掉,故在j=(1001)时应该被加回,以此类推,得到f(x)-f(j)为偶数时容斥系数为1,f(x)-f(j)为奇数时容斥系数为-1.

代码

#include <iostream>
#include <cstdio>
#define nn 100010
#define ll long long
using namespace std;
int read() {
	int re = 0 , sig = 1;
	char c = getchar();
	while(c < '0' || c > '9'){
		if(c == '-')sig = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9')
		re = (re << 1) + (re << 3) + c - '0',
		c =getchar();
	return re * sig;
}
ll C(ll x){
	return x * (x - 1) * (x - 2) / 6;
}
int n , q;
int a[nn];
int cnt[350];
int sum[nn][350];
inline int f(int x){
	int cnt = 0;
	while(x > 0){
		x = x & (x - 1);
		cnt++;
	}
	return cnt;
}
int main(){
	cnt[0] = 0 , cnt[1] = 1 , cnt[2] = 1;
	for(int i = 3 ; i <= 300 ; i++)
		cnt[i] = cnt[i / 2] + (i & 1);
//	for(int i = 1 ; i <= 10 ; i++)
//		cout << i << '	' << cnt[i] << endl;
	n = read() , q = read();
	for(int i = 1 ; i <= n ; i++)
		a[i] = read();
	for(int i = 1 ; i <= n ; i++)
		for(int j = 0 ; j <= 255 ; j++)
			sum[i][j] = sum[i - 1][j] + ((a[i] & j) == a[i] ? 1 : 0);
	for(int i = 1 ; i <= q ; i++){
		int l , r , x;
		long long ans = 0;
		l = read() , r = read() , x = read();
		for(int j = 0 ; j <= 255 ; j++){
			if((j & x) != j)continue;
			int op = cnt[x] - cnt[j];
			if(op & 1)	ans -= C((ll)sum[r][j] - sum[l - 1][j]);
			else		ans += C((ll)sum[r][j] - sum[l - 1][j]);
		}
		printf("%lld
" , ans);
	}
	return 0;
} 

B 堆箱子

题目

题目描述

仓库中有很多的箱子,这天小武想把箱子收拾一下。仓库是一个长方体,我们不用考虑仓库的高度,仓库的长度为 N,宽度为M,箱子是一个标准的正方体,边长为L。小武很严格,对于箱子的摆放有很严格的要求。小武要求用若 干根直线等分仓库的长和宽,而箱子只能放在等分长和宽的直线的交点上,并且一个箱子到四个方向的距离(可能 是到另一个箱子, 也可能是到仓库的边缘),必须相同。那么在满足要求的前提下,小武想放心尽可能多的箱子, 那么此时一个箱子到四个方向的距离是多少呢?(设这个距离为x)
BNFhgf.jpg

输入格式

一行三个数表示L,N,M

输出格式

一行一个实数表示距离x,精确到小数点后五位 如果无法满足要求,输出-1

输入输出样例

输入 #1

2 18 13

输出 #1

0.50000

输入 #2

4 26 26

输出 #2

0.28571

输入 #3

3 46 18

输出 #3

0.50000

说明/提示

数据范围

对于24%的数据,1≤L≤100,1≤N,M≤1000
对于48%的数据,1≤L≤1000,1≤L,N,M≤10^6
对于80%的数据,1≤L≤10^6
对于100%的数据,1≤L,N,M≤10^9

样例解释

对于样例1,横排放7个箱子,竖排放5个箱子,x=(18-27)/8=(13-25)/6=0.50000

思路

其实就是一道数学题,我搞了这么久还WA了证明我太菜了

我们将箱子间的距离看成长度为x的线段

原仓库从横/纵看就是(线段-箱子-线段-箱子……-箱子-线段)的形式

为了方便,我们将仓库扩展到(M+L)*(N+L)

仓库变成(线段-箱子-线段-箱子……-箱子-线段-箱子)的形式,即若干个(线段-箱子)的组合

将一个(线段-箱子)长度设为y(y不一定为整数)

根据题意,则有

[frac{M+L}{y}in , frac{N+L}{y}in  ]

[g=gcd(M+L,N+L) ]

g一定是最大的能同时整除(M+L)和(N+L)的数

根据y的定义,y一定大于等于L

所以

[yin[L,g] ]

题目要我们求的就是最小的(y-L)

因为

[frac{M+L}{g}in,frac{N+L}{g}=in,frac{M+L}{y}in ,frac{N+L}{y}in ,g>=y ]

所以有

[frac{g}{y}in ]

[z=frac{g}{y},(y=frac{g}{z}) ]

又因为

[y>=L, ]

所以

[y=frac{g}{z}>=L ]

所以

[z<=frac{g}{L} ]

又因为z为整数

所以最优解为(y最小,z就要最大):

[z=floor(frac{g}{L}),y=frac{g}{z},ans=y-L ]

输出-1的情况:上面提到,y的取值范围:[L,g]当g<L时,y无解,特别地,若g>M或g>N,也输出-1

代码

#include <iostream>
#include <cstdio>
using namespace std;
int l , m , n;
int gcd(int a , int b) {
	return b == 0 ? a : gcd(b , a % b);
}
int main() {
	cin >> l >> n >> m;
	int g = gcd(n + l , m + l);
	if(g < l || n < l || m  < l)
		cout << "-1";
	else
		printf("%.5lf",1.0 * g / ( g / l) - l);
	return 0;
}

C 快速排序

题目

题目描述

小武最近学习了快速排序,写出了下述的代码

BNkDGq.jpg

这里开始low=0,high=N,数组a为1-N的一个排列
我们采用随机优化的快速排序是很难碰到最坏情况的,但是小林偷偷修改了运行环境,控制了随机数的生成,使得 随机数依次为a1,a2,a3,...,ak,a1,...,即随机数结果依次为a1到ak,然后不断循环。但是还有一个问题,什么样的排列 在这样的随机数下效果最差呢,小林认为效果最差即递归的深度最深,但小林不知道怎么找到这个排列,只好交给 了你

输入格式

第一行两个整数N和k
接下来k行,每行一个数字,表示a1到ak

输出格式

N行整数,为1-N的一个排列,若有多个排列满足条件,输出其中字典序最小的那个排列

输入输出样例

输入 #1

3 1
0

输出 #1

1
2
3

输入 #2

4 2
1
0

输出 #2

1
4
2
3

输入 #3

1 1
0

输出 #3

1

说明/提示

数据范围

对于40%的数据,1=N,k<=10
对于70%的数据,1<=N,k<=10000
对于100%的数据,1<=N,k<=50000, 0<=ai<=10^9

样例解释

对于样例1
(1,2,3) 递归深度为3层,字典序最小
对于样例2
(1, 4, 2, 3)递归深度为4层,字典序最小
(1,2,3,4) -> (1) 2 (3,4) -> 1 2 3 (4) 字典序小,但是只有3层深度

思路

要想卡掉快排就要知道快排的原理:

  1. 在当前区间选定一个标准值(题中的程序用随机选定)
  2. 把比标准值小的放在左边,大的放在右边
  3. 以标准值的下标(经过上述操作)为分界点,进行递归,直到区间长度为1

如果每一次选的标准值都是最小或最大值,那么标准值就会被扔到最左边或最右边,递归的深度就可能达到n,这就是题目所要的

其实卡的方法我知道,但是因为太菜了不会实现,就没有做QAQ

具体模拟及实现题解已经很清楚了:

在这里插入图片描述

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#define nn 50010
using namespace std;
inline void swap(int &x , int &y){
	int tmp;
	tmp = x ; x = y ; y = tmp;
}
int read(){
	int re = 0;
	char c = getchar();
	while(c < '0' || c > '9')
		c = getchar();
	while(c >= '0' && c <= '9')
		re = (re << 1) + (re << 3) + c - '0',
		c = getchar();
	return re;
}
int n , k;
int r[nn] , a[nn] , ans[nn];
int dict[nn];
int main(){
	
	n = read() , k = read();
	for(int i = 0 ; i < k ; i++)
		r[i] = read();
	for(int i = 0 ; i < n ; i++)
		dict[i] = i;
	int left = 0 , right = n - 1;
	int minn = 0;
	memset(ans , 0 , sizeof(ans));
	for(int i = 0 ; i < n ; i++) {
		int p = (r[i % k] % (right - left + 1));
		p += left;
//		cout << p << endl;
		if(dict[p] == minn){
			ans[dict[p]] = left + 1;//这里一定要加1,不然left为0的时候下面找最小会出问题
			swap(dict[p] , dict[left]);
			left++;
			while(ans[minn] != 0)minn++;
			//minn++;
		}
		else {
			ans[dict[p]] = right + 1;
			swap(dict[p] , dict[left]);
			swap(dict[left] , dict[right]);
			right--;
		}
	}
	for(int i = 0 ; i < n ; i++)
		printf("%d
" , ans[i]);
	return 0;
} 
/*
10 5
1
2
3
4
5

1
10
9
8
7
6
2
4
5
3
*/

D 统计学带师

题目

题目描述

BNAFeg.jpg

输入格式

第一行,两个正整数 n, q。
第二行,一个字符串 S。
以下 n 行,第 i 行一个字符串 s_i。
以下 q 行,每行三个正整数 l, r, k。

输出格式

q 行,每行一个整数,表示小 L 的第 i 个问题的答案。

输入输出样例

输入 #1

5 4
abcbcq
abc
bcd
acbc
qcbcba
qwqwq
1 2 2
3 5 4
2 3 3
6 6 1

输出 #1

0
1
1
0

说明/提示

数据范围

对于 40% 的数据,n, q, |S|, ∑|s_i| <= 10^3;
对于 100% 的数据,1 <= n, |S|, ∑|s_i| <= 10^5, 1 <= q <= 2 × 10^5, 1 <= l <= r <= |S|, 1 <= k <= n,所有 字符串均仅包含小写字母

样例解释

BNAGk9.jpg

思路

自动机?没学

在这里插入图片描述

代码(来自参考程序)

#include <cstdio>
#include <vector>
#include <utility>
#include <cstring>
using namespace std;
const int N = 1e5;
const int Q = 2e5;
const int LG = 18;
int n,m,q;
int ed[N + 5],len[N + 5];
char s[N + 5],t[N + 5];
int ans[Q + 5];
namespace SEG
{
    #define ls (p << 1)
    #define rs (ls | 1)
    int seg[(N << 3) + 10];
    void insert(int x,int k,int p,int tl,int tr)
    {
        seg[p] += k;
        if(tl == tr)
            return ;
        int mid = tl + tr >> 1;
        x <= mid ? insert(x,k,ls,tl,mid) : insert(x,k,rs,mid + 1,tr);
    }
    int query(int k,int p,int tl,int tr)
    {
        if(tl == tr)
            return tl;
        int mid = tl + tr >> 1;
        int sum = seg[ls];
        return k <= sum ? query(k,ls,tl,mid) : query(k - sum,rs,mid + 1,tr);
    }
}
namespace SAM
{
    struct node
    {
        int ch[26];
        int fa,len;
    } sam[(N << 1) + 5];
    int las = 1,tot = 1;
    int c[N + 5],a[(N << 1) + 5];
    int sz[(N << 1) + 5],son[(N << 1) + 5];
    vector<int> edp[(N << 1) + 5];
    inline void insert(int x,int pos)
    {
        if(sam[las].ch[x])
        {
            int cur = las,q = sam[cur].ch[x];
            if(sam[cur].len + 1 == sam[q].len)
                las = q,++sz[las],edp[las].push_back(pos);
            else
            {
                int nxt = ++tot;
                sam[nxt] = sam[q],sam[nxt].len = sam[cur].len + 1,sam[q].fa = nxt;
                for(;cur && sam[cur].ch[x] == q;cur = sam[cur].fa)
                    sam[cur].ch[x] = nxt;
                las = nxt,++sz[las],edp[las].push_back(pos);
            }
            return ;
        }
        int cur = las,p = ++tot;
        sam[p].len = sam[cur].len + 1;
        for(;cur && !sam[cur].ch[x];cur = sam[cur].fa)
            sam[cur].ch[x] = p;
        if(!cur)
            sam[p].fa = 1;
        else
        {
            int q = sam[cur].ch[x];
            if(sam[cur].len + 1 == sam[q].len)
                sam[p].fa = q;
            else
            {
                int nxt = ++tot;
                sam[nxt] = sam[q],sam[nxt].len = sam[cur].len + 1,sam[p].fa = sam[q].fa = nxt;
                for(;cur && sam[cur].ch[x] == q;cur = sam[cur].fa)
                    sam[cur].ch[x] = nxt;
            }
        }
        las = p,++sz[las],edp[las].push_back(pos);
    }
    int to[(N << 1) + 5],pre[(N << 1) + 5],first[(N << 1) + 5];
    inline void add(int u,int v)
    {
        static int tot = 0;
        to[++tot] = v,pre[tot] = first[u],first[u] = tot;
    }
    int f[LG + 5][(N << 1) + 5];
    inline void build()
    {
        for(register int i = 1;i <= tot;++i)
            ++c[sam[i].len],i > 1 && (add(f[0][i] = sam[i].fa,i),1);
        for(register int i = 1;i <= LG;++i)
            for(register int j = 2;j <= tot;++j)
                f[i][j] = f[i - 1][f[i - 1][j]];
        for(register int i = 1;i <= N;++i)  
            c[i] += c[i - 1];
        for(register int i = tot;i > 1;--i)
            a[c[sam[i].len]--] = i;
        for(register int i = tot;i > 1;--i)
        {
            sz[sam[a[i]].fa] += sz[a[i]];
            if(!son[sam[a[i]].fa] || sz[son[sam[a[i]].fa]] < sz[a[i]])
                son[sam[a[i]].fa] = a[i];
        }
    }
    inline int get(int p,int d)
    {
        if(sam[p].len < d)
            return 0;
        for(register int i = LG;~i;--i)
            if(f[i][p] && sam[f[i][p]].len >= d)
                p = f[i][p];
        return p;
    }
    vector< pair<int,int> > qry[(N << 1) + 5];
    int cnt[N + 5];
    void dfs(int p)
    {
        for(register int i = first[p];i;i = pre[i])
            if(to[i] ^ son[p])
            {
                dfs(to[i]);
                for(register vector<int>::iterator it = edp[to[i]].begin();it != edp[to[i]].end();++it)
                    SEG::insert(cnt[*it],-1,1,0,N),--cnt[*it],SEG::insert(cnt[*it],1,1,0,N);
            }
        if(son[p])
            dfs(son[p]);
        for(register vector<int>::iterator it = edp[p].begin();it != edp[p].end();++it)
            SEG::insert(cnt[*it],-1,1,0,N),++cnt[*it],SEG::insert(cnt[*it],1,1,0,N);
        if(son[p])
        {
            edp[p].swap(edp[son[p]]);
            for(register vector<int>::iterator it = edp[son[p]].begin();it != edp[son[p]].end();++it)
                edp[p].push_back(*it);
            vector<int>().swap(edp[son[p]]);
        }
        for(register int i = first[p];i;i = pre[i])
            if(to[i] ^ son[p])
                for(register vector<int>::iterator it = edp[to[i]].begin();it != edp[to[i]].end();++it)
                    edp[p].push_back(*it),
                    SEG::insert(cnt[*it],-1,1,0,N),++cnt[*it],SEG::insert(cnt[*it],1,1,0,N);
        for(register vector< pair<int,int> >::iterator it = qry[p].begin();it != qry[p].end();++it)
            ans[it->second] = SEG::query(it->first,1,0,N);
    }
}
int main()
{
    scanf("%d%d%s",&n,&q,s + 1),m = strlen(s + 1);
    for(register int i = 1;i <= n;++i)
    {
        scanf("%s",t),SAM::las = 1;
        for(register char *c = t;*c;SAM::insert(*c++ - 'a',i));
    }
    SAM::build();
    for(register int i = 1,x,p = 1,l = 0;i <= m;++i)
    {
        x = s[i] - 'a';
        if(SAM::sam[p].ch[x])
            p = SAM::sam[p].ch[x],++l;
        else
        {
            for(;p && !SAM::sam[p].ch[x];p = SAM::sam[p].fa);
            !p ? (p = 1,l = 0) : (l = SAM::sam[p].len + 1,p = SAM::sam[p].ch[x]);
        }
        ed[i] = p,len[i] = l;
    }
    int l,r,k;
    for(register int i = 1;i <= q;++i)
        scanf("%d%d%d",&l,&r,&k),
        len[r] >= r - l + 1 && (SAM::qry[SAM::get(ed[r],r - l + 1)].push_back(make_pair(k,i)),1);
    SEG::insert(0,n,1,0,N),SAM::dfs(1);
    for(register int i = 1;i <= q;++i)
        printf("%d
",ans[i]);
}
原文地址:https://www.cnblogs.com/dream1024/p/13957762.html