• 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]);
    }
    
  • 相关阅读:
    例图作业
    冲刺第二天
    Day9
    Day8
    Day7
    Day6
    Day5
    Day4
    Day2
    第四次作业——个人作业——软件案例分析
  • 原文地址:https://www.cnblogs.com/dream1024/p/13957762.html
Copyright © 2020-2023  润新知