C/C++:看简介吧= ̄ω ̄=

小红的彩带

小红有一条长度为 n 的彩带,彩带的每一个位置都有一个颜色,用 ai表示。
小红每次会从左往后或从右往左剪一段长度为 x 的彩带,她想知道她每次剪下来的彩带有多少种颜色。

输入描述:
第1行输入2个正整数n,q(1<=n,q<=105),n表示彩带长度,q表示剪彩带次数

第2行输入n个正整数ai(1<=ai<=109)表示彩带每一个位置的颜色

接下来q行,每行先输入1个字符c,c为'L'说明从左到右,c为'R'从右往左剪,再输入1个正整数x(1<=x<=n).

保证x的总和不超过n

输出描述:
输出q行,每行输出一个整数表示答案

示例:
输入:

6 3
1 1 4 5 1 4
L 3
R 2
L 1

输出:

2
2
1

解题思路:

  • 将彩带看作线段,设两个变量lr,作为线段的两端,那一端被剪,就对那一端加或减
  • 比如c为’L’,x=3时,那么l=l+3,r同理
  • 想知道剪下来的有多少种颜色,也就是有多少个不同的数,也就是剪下来的那段线段,去重之后的数组长度,就是答案

参考代码:

C++:

#include <iostream>
#include <vector>
#include <set>
using namespace std;

int main() {
    int n, q;
    cin >> n >> q;

    vector<int> colors(n);//存储彩带n个位置的颜色
    for (int i = 0; i < n; ++i) {
        cin >> colors[i];
    }
        int l=0,r=n;//剪去彩带后的边界,
    for (int i = 0; i < q; ++i) {//q次剪彩带
        char c;
        int x;
        cin >> c >> x;

        set<int> cut_colors;//这个set容器在这就格外好用了,自动去重省不少事
        if (c == 'L') {
            for (int j = l; j < l+x&&j<r; ++j) {
                cut_colors.insert(colors[j]);
            }
            l+=x;
        } else {
            for (int j = r-1; j >= r-x&&j>=l; --j) {
                cut_colors.insert(colors[j]);
            }
            r-=x;
        }

        cout << cut_colors.size() << endl;//输出剪下线段的数组长度
    }

    return 0;
}

用C,要自己写个数组去重,其他的没什么区别
C:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef long long ll;

int removeDuplicates(ll arry[], int size) {
    if (size == 0) {
        return 0;
    }
    int low = 1, high = size - 1;
    while (low <= high) {//就是每次遍历0到low这一段,遍历到其中重复的元素,往最后面扔
        for (int i = 0; i < low; i++) {
            if (arry[i] == arry[low]) {
                int var = arry[high];
                arry[high] = arry[low];
                arry[low] = var;
                high--;
                low--;
                break;
            }
        }
        low++;
    }
    return high + 1;
}

int main() {
    int n, q;
    scanf("%d%d", &n, &q);
    ll a[n];
    for (int i = 0; i < n; i++)
        scanf("%lld", &a[i]);
    int l = 0, r = n;
    while (q--) {
        char c;
        int x;
        scanf(" %c %d", &c, &x);
        ll b[x];
        memset(b, 0, sizeof(b));
        int size = 0;
        if (c == 'L') {
            for (int i = l; i < l + x && i < r; i++)
                b[size++] = a[i];
            l += x;
        } else {
            for (int i = r - 1; i >= r - x && i >= l; i--) {
                b[size++] = a[i];
            }
            r -= x;
        }
        size = removeDuplicates(b, size);//返回数组去重之后的数组长度
        printf("%d\n", size);
    }
    return 0;
}

祖玛游戏

小红喜欢玩祖玛游戏,游戏规则如下:

  1. 游戏中会有一条路径,一群彩色的球沿着这条路径滚动前进。
  2. 玩家发射出彩色的球,使得发射的球射入滚动的球中。
  3. 如果有 3 个或更多数量、颜色相同的球连在一起时,这些球就会被消除。

现在小红有一个长度为n的数组,数组中的每个元素代表一种颜色的球,小红有一次机会调整数组中球的顺序,问最少需要添加多少个球可以消除所有球。

输入描述:
第一行一个整数n,表示数组的长度。
第二行n个整数ai,表示每个球的颜色。
1<=n<=105
1<=ai<=109

输出描述:
输出一个整数,表示最少需要添加多少个球可以消除所有球。

示例:

输入:

5
1 2 2 3 1

输出:

4

说明:
重新排列成[1,1,2,2,3],这样再添加1个1号球1个2号球和2个3号球即可消除所有球。

解题思路:

这题主要是可能理解错题目,游戏规则的第三条意思是有3个球或以上连在一起就会自动消失了,不需要再打进一个球

这题理解题意就很简单了,其他的就不用说了

参考代码:

C++:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
    int n;
    cin>>n;
    ll a[n];
    for(int i=0;i<n;i++)
    cin>>a[i];
    sort(a,a+n);//排序,也就是调整组中球位置,方便把相同的球汇聚在一起
    int count=0;
    map<ll,int>sum;//first是球的颜色,second是球的数量
    for(int i=0;i<n;i++)
    {
        sum[a[i]]++;//统计出每种颜色球的数量
    }
    for(auto m:sum)//遍历map
    {
        int x=m.second;
        if(x<3)//当球的数量小于3,就要打进一定量的球,让球的数量等于3
        {
            count+=3-x;
        }
    }
    cout<<count;
    return 0;
}

C:

#include <stdio.h>
typedef long long ll;
int n;
void swap(ll *x,ll *y)
{
    ll t=*x;
    *x=*y;
    *y=t;
}
int partition(ll a[],int low,int high)
{
    ll pivot=a[high];
    int i=low-1;
    for(int j=low;j<high;j++)
    {
        if(a[j]<pivot)
        {
            i++;
            swap(&a[j],&a[i]);
        }
    }
    swap(&a[i+1],&a[high]);
    return i+1;
}
void quickSort(ll a[],int low,int high)
{
    if(low<high)
    {
        int pi=partition(a,low,high);
        quickSort(a,low,pi-1);
        quickSort(a, pi+1, high);
    }
}
int main() {
    scanf("%d", &n);
    ll a[n];
    for(int i=0;i<n;i++)
    scanf("%lld",&a[i]);
    quickSort(a,0,n-1);//快速排序
    int count=0;
    for(int i=0;i<n;i++)//排序后,再数相同的数就容易多了
    {
        int count_1=1;
        if(a[i]==a[i+1]&&i<n-1)
        {
            while(a[i]==a[i+1]&&i<n-1)
            {
                count_1++;
                i++;
            }
        }
        if(count_1<3)
        count+=3-count_1;
    }
    printf("%d",count);
    return 0;
}

小红的数组构造

小红希望你构造一个长度为n的数组,满足所有元素之和等于x,并且所有元素的按位或恰好等于y。

输入描述:
三个正整数n,x,y,用空格隔开。
1<=n<=104
1<=x,y<=109

输出描述:
如果无解,请输出-1
否则输出n个非负整数,代表小红构造的数组。如果有多解,输出任意一个即可。

示例:

输入:

4 5 2

输出:

-1

解题思路:

  • 先要理解所有元素按位或的意思,就是a[0]|a[1]|a[2],就是构造的数组元素互相或最后的结果要为y,
  • 这也就明白了一点,数组里每一个元素都小于或等于y,而且x必定大于y才会才会存在这个数组,举个例子,如果y是7,要想数组元素按位或的结果等于y,7的二进制为0111,数组元素至少要为1,2,4,或有个7
  • 提取出y的二进制位中的为1的值,拼凑数组,保证最终结果等于y

参考代码:

C++:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
ll x, y;
int a[50] = {0};
ll b[50] = {0};
int count1, count2 = 0;
void fun() {
    if (x < y) {//如果x<y,数组不存在
        cout << -1;
        return;
    }
    count1 = 0;//y的二进制位数
    count2 = 0;//y的二进制中为1的数
    ll z=y;
    while (z) { //取出y的二进制
        a[count1++] = z % 2;
        z >>= 1;
    }
    for (int i = 0; i < count1; i++) { //提出y的二进制为1的值
        if (a[i] == 1) {
                b[count2++] = pow(2, i);
        }
    }
    vector<ll>ans(n, 0);//最终构造数组
    ans[0]=y;
    x-=y;
    for(int i=count2-1;i>=0;i--)//保证能在n个元素之和等于x,从最大的开始遍历递减
    {
        for(int j=1;j<n;j++)//构造数组
        {
            if(x>=b[i])
            {
                ans[j]+=b[i];
                x-=b[i];
            }
        }
    }
    if (x != 0) {//保证结果所有元素之和符合等于x
        cout << -1;
        return;
    }
    for (int i = 0; i < n; i++)
        cout << ans[i] << ' ';
}
int main() {
    cin >> n;
    cin >> x >> y;
    fun();
    return 0;
}

优化一下,上面用来两个数组,y转二进制还有些麻烦
C:

#include <stdio.h>
typedef long long ll;
ll qpow(int x,ll y)//快速幂,写完后又不想用了╰( ̄ω ̄o) 
{
    ll res=1;
    while(y)
    {
        if(y&1)
        res*=x;
        x*=x;
        y>>=1;
    }
    return res;
}
int main() {
    int n;
    ll x,y;
    scanf("%d%lld%lld",&n,&x,&y);
    if(y>x)
    {
        printf("-1");
        return 0;
    }
    ll a[n];
    memset(a,0,sizeof(a));
    a[0]=y;
    x-=y;
    for(int i=32;i>=0;i--)//从高位到低位遍历y的二进制
    {
        if(y>>i&1)//当前遍历到的位为1时,对于每一位为1的位置,尝试将剩余的和x用该位置的值补充到数组中
        for(int j=1;j<n;j++)
        {
            if(x>=(1<<i))
            {
                a[j]+=1<<i;
                x-=1<<i;
            }
        }
    }
    if(x!=0)
    {
        printf("-1");
        return 0;
    }
    for(int i=0;i<n;i++)
    printf("%lld ",a[i]);
    return 0;
}

小红的特殊数组

小红定义一个数组为特殊数组:这个数组大小至少为 3 ,只由 2 种数字组成,且其中一种数字恰好出现一次。

例如:[1,1,4],[1,4,1] 是特殊数组(长度为3,只有1和4组成,4恰好出现一次),[1,4] ,[5,1,4] ,[1,1,1] 都不是特殊数组。

小红有一个长度为 n 的数组 a ,她想知道这个数组有多少个子序列是特殊数组。

输入描述:
第1行输入1个正整数n(1<=n<=105),表示数组长度。
第2行输入n个正整数ai(1<=ai<=109)表示数组。

输出描述:
输出一个整数表示答案。由于答案可能很大,输出答案对109+7取模的结果

示例:

输入:

4
1 1 4 5

输出:

2

说明
子序列[1,1,4],[1,1,5]是特殊数组。

解题思路:

对于这道题,如果能想到组合数的话,就好做了

  • 先统计出各个相同数字的数量,只关注有相同的数字,
  • 这时候就能列出组合公式了,遍历数字,从n-当前数字相同数字数挑一个数进入该子序列,又因为位置的不同,会产生新序列,所以还要再开个for循环遍历相同数字数,i从2开始累加,进入该子序列,
  • 也就是count=count+C(n-当前数字相同数字数)*C(当前数字相同数字数,i),遍历完所有有相同的数字后,就是结果
  • 因为组合数涉及到了除法,最后的结果可能是分数,又要取模,为了保证结果的正确,要把除法变成逆元的形式
  • 简要说下逆元:根据 逆元 的定义,如果你最后得到的答案是形如a/b的分数,之后你需要对𝑝 取模的话,你需要输出(a*bp-2) mod p来保证你的答案是正确的

参考代码:

C++:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const ll N=1e5;
ll cnt[100000],cnt1[100000];//cnt是阶乘,cnt1是逆元阶乘,用于方便计算组合数
ll qpow(ll a,ll b)//快速幂函数,处理逆元时用到
{
    ll res=1;
    while(b)
    {
        if(b&1)
        res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
ll inverse(ll x,ll y)//计算组合数,C(x,y)=cnt[x]/(cnt[y]*cnt1[x-y]),这是原本的式子,代码中是根据逆元变换的
{
    return cnt[x]*cnt1[y]%mod*cnt1[x-y]%mod;
}
int main() {
    cnt[0]=1;
    for(int i=1;i<N;i++)
    {
        cnt[i]=cnt[i-1]*i%mod;
    }
    cnt1[N-1]=qpow(cnt[N-1],mod-2)%mod;//cnt1[N-1]=1/cnt[N-1],但我们显然不能这样,就要逆元处理一下
    for(int i=N-1;i>0;i--)
    {
        cnt1[i-1]=cnt1[i]*i%mod;//分母是在减小的,所以乘以i
    }
    int n;
    cin>>n;
    unordered_map<ll,ll>number;//用一个map数组把相同的数分列出来
    for(int i=0;i<n;i++)
    {
    ll a;
    cin>>a;
    number[a]++;        
    }

    ll count=0;//统计有多少个子序列符合
    for(auto m:number)//遍历
    {
        if(m.second>=2)//只处理有相同数字的
        {
                for(int i=2;i<=m.second;i++)
                /*子序列最少要有两个相同的数,然后从n-m.secnod也就是剩下不同的数里挑一个,
                再从m.second中选出i个组成子序列,位置不同,也是算不同的序列
                比如题目就给出了例子,[1,1,4]和[1,4,1]*/
                {
                    count=(count+(n-m.second)%mod*inverse(m.second,i)%mod)%mod;
                }
        }
    }
    cout<<count;
    return 0;
}

难绷,谁要用C写算法,头不得炸,对于极大的数去统计这个数相同的个数,对于C似乎麻烦,我第一时间想到的是先排序,再开个数组来统计
结果哪怕用快排也不行,超时了
C:

#include <stdio.h>
typedef long long ll;
const ll mod = 1e9 + 7;
#define N 100000
ll cnt[100000], cnt1[100000];
ll number[100000];//number是统计各组相同数的数量
ll qpow(ll x, ll y) {
    ll res = 1;
    while (y) {
        if (y & 1)
            res = res * x % mod;
        x = x * x % mod;
        y >>= 1;
    }
    return res;
}
void swap(ll* a, ll* b) {
    ll t = *a;
    *a = *b;
    *b = t;
}
int partition(ll a[], int low, int high) {
    ll pivot = a[high];
    int i = low - 1;
    for (int j = low ; j < high; j++) {
        if (a[j] < pivot) {
            i++;
            swap(&a[i], &a[j]);
        }
    }
    swap(&a[i + 1], &a[high]);
    return i + 1;
}
void quickSort(ll a[], int low, int high) {//快排
    if (low < high) {
        int pi = partition(a, low, high);
        quickSort(a, low, pi - 1);
        quickSort(a, pi + 1, high);
    }
}
ll inverse(ll x, ll y) { //计算组合数,C(x,y)=cnt[x]/(cnt[y]*cnt1[x-y]),这是原本的式子,代码中是根据逆元变换的
    return cnt[x] * cnt1[y] % mod * cnt1[x - y] % mod;
}
int main() {
    cnt[0] = 1;
    for (int i = 1; i < N; i++) {
        cnt[i] = cnt[i - 1] * i % mod;
    }
    cnt1[N - 1] = qpow(cnt[N - 1], mod - 2) % mod;
    for (int i = N - 1; i > 0; i--) {
        cnt1[i - 1] = cnt1[i] * i % mod;
    }
    int n;
    scanf("%d", &n);
    ll a[n];
    for (int i = 0; i < n; i++)
        scanf("%lld", &a[i]);
    quickSort(a, 0, n - 1);
    int _count=0;
    for(int i=0;i<n;i++)
    {
        number[_count]=1;
        if(a[i]==a[i+1]&&i<n-1)
        {
            while(a[i]==a[i+1]&&i<n-1)
            {
                number[_count]++;
                i++;
            }
        }
        _count++;
    }
    ll count=0;
    for(int i=0;i<_count;i++)
    {
        if(number[i]>=2)
        {
            int m=number[i];
            for(int j=2;j<=m;j++)
            {
                count=(count+(n-m)%mod*inverse(m,j)%mod)%mod;
            }
        }
    }
    printf("%lld",count);
    return 0;
}

斯,是我井底之蛙了,竟然还有qsort(),C也有堪比sort的函数,哈哈,这可比我写的快排快多了
简单说下:

  • qsort 是 C 标准库中提供的一个函数,用于对数组进行快速排序。它在 <stdlib.h> 头文件中定义。qsort 使用的是快速排序算法(quicksort),这是一种高效的排序算法,平均时间复杂度为 O(n log n)。
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void *));

参数:
base: 指向待排序数组的第一个元素的指针。
nitems: 数组中的元素数量。
size: 数组中每个元素的大小(以字节为单位)。
compar: 比较函数的指针,该函数用于比较两个元素。比较函数应当返回一个整数,表示比较结果:
小于零:表示第一个元素小于第二个元素。
等于零:表示两个元素相等。
大于零:表示第一个元素大于第二个元素。

优化后能过的,其实就是不用我的快排,用qsort就行,不过还要写个cmp函数:
C:

#include <stdio.h>
#include<stdlib.h>
typedef long long ll;
const ll mod = 1e9 + 7;
#define N 100000
ll cnt[100000], cnt1[100000];
ll number[100000];
ll qpow(ll x, ll y) {
    ll res = 1;
    while (y) {
        if (y & 1)
            res = res * x % mod;
        x = x * x % mod;
        y >>= 1;
    }
    return res;
}
void swap(ll* a, ll* b) {
    ll t = *a;
    *a = *b;
    *b = t;
}
int partition(ll a[], int low, int high) {
    ll pivot = a[high];
    int i = low - 1;
    for (int j = low ; j < high; j++) {
        if (a[j] < pivot) {
            i++;
            swap(&a[i], &a[j]);
        }
    }
    swap(&a[i + 1], &a[high]);
    return i + 1;
}
void quickSort(ll a[], int low, int high) {
    if (low < high) {
        int pi = partition(a, low, high);
        quickSort(a, low, pi - 1);
        quickSort(a, pi + 1, high);
    }
}
ll inverse(ll x, ll
           y) { //计算组合数,C(x,y)=cnt[x]/(cnt[y]*cnt1[x-y]),这是原本的式子,代码中是根据逆元变换的
    return cnt[x] * cnt1[y] % mod * cnt1[x - y] % mod;
}

int cmp(const void* a, const void* b) {//对于整型排序
    return *(ll*)a - *(ll*)b;
}
int main() {
    cnt[0] = 1;
    for (int i = 1; i < N; i++) {
        cnt[i] = cnt[i - 1] * i % mod;
    }
    cnt1[N - 1] = qpow(cnt[N - 1], mod - 2) % mod;
    for (int i = N - 1; i > 0; i--) {
        cnt1[i - 1] = cnt1[i] * i % mod;
    }
    int n;
    scanf("%d", &n);
    ll a[n];
    for (int i = 0; i < n; i++)
        scanf("%lld", &a[i]);
    qsort(a, n, sizeof(ll), cmp);//好用,哈哈
    int _count = 0;
    for (int i = 0; i < n; i++) {
        number[_count] = 1;
        if (a[i] == a[i + 1] && i < n - 1) {
            while (a[i] == a[i + 1] && i < n - 1) {
                number[_count]++;
                i++;
            }
        }
        _count++;
    }
    ll count = 0;
    for (int i = 0; i < _count; i++) {
        if (number[i] >= 2) {
            int m = number[i];
            for (int j = 2; j <= m; j++) {
                count = (count + (n - m) % mod * inverse(m, j) % mod) % mod;
            }
        }
    }
    printf("%lld", count);
    return 0;
}
  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值