SDU 第七周CSP模拟题

SDU 第七周CSP模拟题

A

解:数一下每种颜色袜子的个数,再除以2加到答案里即可

#include <iostream>
#include <cmath>
using namespace std;
const int N = 100010;
void in(int &x){
	scanf("%d",&x);
}
void o(int x){
	printf("%d",x);
}
int n,k;
int cnt[N];
int main() {
    in(n);in(k);
    int ans=0;
    for(int i=1;i<=n;i++){
        int x;in(x);
        cnt[x]++;
        if(cnt[x]>1){
            cnt[x]-=2;
            ans++;
        }
    }
    o(ans);putchar('\n');
    return 0;
}

B

解:

使用一个数组(以差分形式代码中的chafen[])记录任务认为每一个价格合理人数。

那么认为价钱为i元是合理的 的人数是数组中 从1至i的和

如:任务价钱4元合理的人数为  chafen[1]+chafen[2]+chafen[3]+chafen[4]

维护方法:

对于题中的每一个 [l,r]

chafen[l]加一,chafen[r+1]减一

#include <iostream>
#include <cmath>
using namespace std;
const int N = 1000010;
void in(int &x){
	scanf("%d",&x);
}
void o(int x){
	printf("%d",x);
}
int n;
int chafen[N];//我们需要维护的差分数组
int a[N];//还原后的数组
signed main(){
    in(n);//输入l,r对的个数
    for(int l,r,i=1;i<=n;i++){
        in(l);in(r);
        chafen[l]+=1;
        chafen[r+1]-=1;
    }
    int maxx=0;//记录大值人数,初始化为0
    for(int i=1;i<=n;i++){
        a[i]=a[i-1]+chafen[i];
        maxx=max(maxx,a[i]);
    }
    int p=1;
    for(int i=1;i<=n;i++){//根据最大人数找出最高的价格
        if(a[i]==maxx)p=i;
    }
    o(p);putchar(' ');o(maxx);putchar('\n');//输出答案
    return 0;
}

C

80分:

注意到任意一区间(l,r)存在以下两个性质:

性质一:若 (L,R)合法,则(L,R+1), (L,R+2), (L,R+3) ,,,,,, (L,x) 等均合法。(因为剩下的数在变少)

性质二:若(L,R)不合法,则(L+1,R)也不合法。(因为剩下的数在变多了)

通过性质一,对于每一个L,若找到其对应的最小的合法的右端点R,那么以L为左端点的合法的区间数目为 x-R+1 ,讲其记作dp[L]。

再根据性质二,我们若已知 L1  对应的最小的 R1  ,那么L1+1,对应的 R应该大于等于R1,于是我们可以从R1开始搜索得到L1+1 对应的最小的合法的R

对于区间合法性的判断采用暴力搜索的办法判定。

总时间复杂度 为 n2log(n)

合法==符合题目要求

#include <iostream>
using namespace std;
const long long N = 1000010;
void in(long long &x){
    x=0;char c=getchar();
    long long y=1;
    while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    x*=y;
}
void o(long long x){
    if(x<0){x=-x;putchar('-');}
    if(x>9)o(x/10);
    putchar(x%10+'0');
}
long long n,x;//包子个数和包子重量上限
long long a[N];//包子重量
long long dp[N];//最小的右端点
bool check(long long l,long long r){
    if(r>x)return true;//r已经加到不存在的区间了,直接退出
    long long last = -1;//判断过程中的上一个数
    for(long long i=1;i<=n;i++){
        if(l<=a[i]&&a[i]<=r)continue;//跳过被删除了的数
        if(last!=-1){//是不是开头
            if(a[i]<last)
            	return false;//比上一个数小,说明不是非递减数组,返回false
        }
        last=a[i];//记下上一个每被删的数
    }
    return true;//是非递减数组
}
signed main(){
    in(n);in(x);//输入包子个数和包子重量上限
    for(long long i=1;i<=n;i++)in(a[i]);//输入wi
    for(long long i=1;i<=x;i++){
        long long r=max(i,dp[i-1]);
        //找出左端点为i的合法的最小的右端点r,从max(i,dp[i-1])开始找
        while(!check(i,r))//如果当前r右端点不合法,令其+1
            r++;
        dp[i]=r;
    }
    long long ans=0;//答案
    for(long long i=1;i<=x;i++){
        ans+=(x-dp[i]+1);//统计答案
    }
    o(ans);putchar('\n');//输出答案
    return 0;
}

100分

利用set容器对上面做法中 判断的区间合法的过程 进行优化。(由 O(n)降低为O(log(n))

由于我们每次判断的区间和上一次判断的相比要么是左端点加一,要么是右端点加一。

他们是连续的。

我们用一个stl::set 《int》st维护删除(L,R)的包子后,还剩下包子的索引。

若 删除区间 的左端点L加了1,相当于将大小为L的包子的索引添加到st中,

右端点R加了1,相当于将大小为R的包子的索引从st中删除。

再定义一个变量cnt,用来维护当前数组中有多少对相邻的逆序的数,即多少个t,a[t]>a[t+1]

当cnt为0的,当前数组是合法的,反之则不合法

#include <bits/stdc++.h>
using namespace std;
const long long N = 1000010;
void in(long long &x){
    cin>>x;
}
void o(long long x){
    cout<<x;
}
long long n,x;
long long cnt;//逆序数量
long long a[N],dp[N];//包子,最小的右端点
set<long long>st;//当前数组
vector<long long>g[N];//  g[i]中包含重量为i的所有的包子的位置
void Add(long long x){//  把大小为x的包子加入数组中
    for(long long i=0;i<g[x].size();i++){
        auto it = st.lower_bound(g[x][i]);//二分查找的到第一个大于等于g[x][i]的元素在st的中的迭代器。
        long long last=-1,next = -1;//last 用于记录 g[x][i]的前一位,next是后一位,他们均有可能不存在
        if(it!=st.end())next=*(it);
        if(it!=st.begin())it--,last=*(it);
        if(next!=-1&&last!=-1){//均存在
            if(a[next]<a[last])cnt--;
            if(x<a[last])cnt++;
            if(a[next]<x)cnt++;
        }else if(next!=-1){//next存在
            if(a[next]<x)cnt++;
        }else if(last!=-1){//last存在
            if(x<a[last])cnt++;
        }
        st.insert(g[x][i]);//将g[x][i]加入到st中
    }
}
void Drop(long long x){//  将大小为x的包子从数组中删去
    for(long long i=0;i<g[x].size();i++){
        auto it = st.lower_bound(g[x][i]);
        long long next = -1,last = -1;
        it++;if(it!=st.end())next = *(it);
        it--;
        if(it!=st.begin()){
            it--;
            last = *(it);
            it++;
        }
        if(last!=-1){
            if(a[last]>x)cnt--;
        }
        if(next!=-1){
            if(a[next]<x)cnt--;
        }
        if(next!=-1&&last!=-1){
            if(a[next]<a[last])cnt++;
        }
        st.erase(g[x][i]);
    }
}
bool check(){
    return cnt==0;
}
signed main(){
    in(n);in(x);
    for(long long i=1;i<=n;i++){
        in(a[i]);
        g[a[i]].push_back(i);//将下标i加入到g[a[i]]中
    }
    for(long long i=1;i<=x;i++)Add(i);//  把所有包子加进去
    long long r = 1;
    for(long long  i=1;i<=x;i++){
        Add(i-1);//左端点i加了1,删除重量为i-1的包子
        if(r==i){//特判一下,右端点不能等于左端点
            Drop(r);//删掉r
            r++;
        }
        while(!check()){
            if(r==x+2)break;//没有有r与i对应了,退出循环
            Drop(r);r++;//删掉r
        }
        dp[i]=r-1;//记录答案
    }
    long long ans=0;//统计答案
    for(long long i=1;i<=x;i++){
        ans+=(x-dp[i]+1);
    }
    o(ans);
    putchar('\n');
    return 0;
}
/*
 5 5
 1 3 2 2 4

 */
posted @ 2021-04-17 12:42  yesuweiYYYY  阅读(96)  评论(0编辑  收藏  举报