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
*/