Codeforces Round #481 (Div. 3) 解题报告

A - Remove Duplicates

题目大意:

现在有一个长度为n的一串数字,要将其中相同的数字删掉。删除的规则:如果数字相同时保留最右边的那一个。输出删除之后的数字串

大致思路:

暴力模拟情况就好···虽然我感觉我写的非常复杂,应该有更简单的写法。

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
int a[55];
struct Node{
	int pos,id;
	Node(){pos=0;id=0;}
}pos[maxn];
bool visit[maxn];
bool cmp(Node a,Node b)
{
	return a.pos<b.pos;
}
int main()
{
	ios::sync_with_stdio(false);
	memset(visit,false,sizeof(visit));
	int n,cnt=0;
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>a[i];
		if(pos[a[i]].pos==0)
			cnt++; //数有多少个数字
		pos[a[i]].pos = i;//记录某一个数出现的最后位置
		pos[a[i]].id = a[i];
	}
	vector<Node> q;
	cout<<cnt<<endl;
	for(int i=1;i<=n;++i){//把结果放到数组里就可以了
		if(visit[a[i]]==false){
			q.push_back(pos[a[i]]);
			//cout<<pos[a[i]].pos<<" "<<pos[a[i]].id<<endl;
			visit[a[i]]=true;
		}
	}
	sort(q.begin(),q.end(),cmp);
	for(int i=0;i<cnt;++i)
		cout<<q[i].id<<" ";
	cout<<endl;
	return 0;
}

B - File Name

题目大意:

现在有一个长度为n的字符串,现在想让字符串中不存在连续的三个x字符。问需要删除多少个字符。

大致思路:

也是暴力模拟就好,循环里一个计数器,看连续的x的个数

代码:

#include<bits/stdc++.h>
using namespace std;
char str[110];
int main()
{
	int n,ans=0,cnt=0;
	cin>>n>>str;
	for(int i=0;i<n;++i){
		//cout<<i<<" "<<cnt<<" "<<ans<<endl;
		if(str[i]!='x') cnt=0;
		else cnt++;
		if(cnt==3){
			ans++;
			cnt--;
			
		}
	}
	//cout<<str<<endl;
	cout<<ans<<endl;
	return 0;
}

C - Letters

题目大意:

题目背景比较复杂,现在简化一下:

\(n\) 个房间,按 $ 1...n$ 进行编号, 每个房间里有 \(a_i\) 个格子。

\(m\) 个询问,每个询问给出一个数字,代表着我现在要找的是第 \(x\) 个格子

这个 \(x\) 是对于全局的。比如我现在第一个房间里有10个,第二个房间里有10个。那么第二个房间里的格子的序号就是从 \(11...20\)

要求对于每一个询问,输出这个格子所在的房间号和这个格子相对于这个房间的序号。

解题思路:

对于 \(a_i\) 求一个前缀和。对于每一个询问在前缀和里面进行二分,答案就很显然的出来了。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+7;
ll a[maxn],b[maxn],pre[maxn]={0};
int main()
{
	ios::sync_with_stdio(false);
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;++i)
		cin>>a[i];
	for(int i=1;i<=m;++i)
		cin>>b[i];
	for(int i=1;i<=n;++i)
		pre[i] = a[i]+pre[i-1];
	for(int i=1;i<=m;++i){
		int pos = lower_bound(pre+1,pre+1+n,b[i]) -(pre+1);
		cout<<pos+1<<" "<<b[i] - pre[pos]<<endl;
	}
	
	return 0;
}

吐槽一下:这个题给了4s,是想让 \(O(nm)\) 的暴力也能过吗···


D - Almost Arithmetic Progression

题目大意:

有一个长度为 \(n\) 的数字序列,你可以对每一个数字进行一次 \(+1\) 或者 \(-1\) 的操作。

使得这个序列变成一个等差序列。现在求最小的操作次数,如果不能变成等差序列,就输出 \(-1\)

解题思路:

对于整个序列枚举公差就好了。

先将数列的最大值和最小值得到,假设为 \(maxx\)\(minl\)

现在设公差为 \(d\) ,序列长度为 \(n\)

那么可以很显然的得到:

\(d\times (n-1) = (maxx-minl-2) \ldots (maxx-minl+2)\)

然后这个题有一个 \(trick\) ,就是公差可以是负数,所以就需要扫两次。正向一个,反向一次

这个算法的最坏情况下就是 \(2\times 4\times 3 \times n\) 次循环,也就是 \(2e6\)

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF =1<<30;
const int maxn=1e5+7;
int a[maxn];
int main()
{
    //freopen("in.txt","r",stdin);
	ios::sync_with_stdio(false);
	int n;
	cin>>n;
	for(int i=1;i<=n;++i)
        cin>>a[i];
    if(n<=2){
        cout<<0<<endl;
        return 0;
    }

	int maxx = -INF,minl = INF,ans=1<<30;
	for(int i=1;i<=n;++i)
        maxx = max(maxx,a[i]),minl = min(minl,a[i]);

	//cout<<maxx<<" "<<minl<<endl;
	int d;
	bool flag=false;
	for(int i=maxx-minl-2;i<=maxx-minl+2;++i){ //正向
		if(i%(n-1)==0){
			d=i/(n-1); //枚举公差
			ll now;
			//cout<<d<<endl;
			for(int i=-1;i<=1;++i){
				int cnt=0,x;
				now = a[1] +i;
				for(int i=1;i<=n;++i){
					x=abs(now-a[i]);
					if(x>1){now=0;break;}
					if(x!=0) cnt++;
					now+= d;
				}
				if(now){
                    //cout<<d<<" "<<a[1]+i<<endl;
					ans =min(ans,cnt);
					flag=true;
				}
	 		}
		}
	}
	if(!flag)
	for(int i=minl-maxx-2;i<=minl-maxx+2;++i){ //反向
		if(i%(n-1)==0){
			d=i/(n-1);
			ll now;
			//cout<<d<<endl;
			for(int i=-1;i<=1;++i){
				int cnt=0,x;
				now = a[1] +i;
				for(int i=1;i<=n;++i){
					x=abs(now-a[i]);
					if(x>1){now=0;break;}
					if(x!=0) cnt++;
					now+= d;
				}
				if(now){
                    //cout<<d<<" "<<a[1]+i<<endl;
					ans =min(ans,cnt);
					flag=true;
				}
	 		}
		}
	}
	if(flag)
		cout<<ans<<endl;
	else
		cout<<-1<<endl;
	return 0;
}

E - Bus Video System

题目大意:

有一辆公交车,现在知道连续 \(n\) 个站的上下人数之和,还有车的容量。

问这个车在开到这n个站之前的人数可能情况有多少种。

解题思路:

将情况简单模拟一下,就可以知道有以下的规律:

在这 \(n\) 个站的过程中,出现的最大数大于0,说明之前肯定有至少有这么多的空位。

若出现的最小数<0,则说明之前肯定至少有这么多人已经在车上了。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF =1<<30;
int main()
{
    int n,w,num;
    while(cin>>n>>w)
    {
        int now=0,maxx=-INF,minl=INF;
        for(int i=0;i<n;++i){
            cin>>num;
            now+=num;
            maxx = max(maxx,now);
            minl = min(minl,now);
        }
        int ans = w+1;
        if(minl!=-INF && minl<0)
            ans+=(minl);
        if(maxx!=INF && maxx >0)
            ans-=(maxx);
        if(ans<0)
            ans=0;
        //cout<<maxx<<" "<<minl<<endl;
        cout<<ans<<endl;
    }
    return 0;
}

F - Mentors

题目大意:

\(n\) 个人,每一个人有一个技能值 \(r\)\(m\) 对人之间有争吵。现在定义一个关系:教师

\(\forall a,b \in n , r_a > r_b\)\(a\)\(b\) 之间没有争吵,那么 \(a\) 可以做 \(b\) 的教师

现在问对于每一个人,能做多少个人的教师?

解题思路:

给每一个人建一个set。

意义为:\(\forall x \in set[a] , r_a > r_x\)

也就是说 \(a\) 可以做 \(x\) 的教师

然后把每个人的信息按技能值排序。到时候看排序后的位置减去 \(set\) 的大小就可以了。

但这里还涉及到一个问题:存在多个人技能值相同 。

也就是说不能直接按照排序后的位置直接当成答案,还需要去找到第一个比自己技能值小的人的位置。

最简单暴力的方法就是每一次循环暴力找

如我第一次提交的代码:

for(int i=n;i>=1;--i){
        int j=i-1;
        while(pro[i].r<=pro[j].r) //暴力寻找第一个比i小的位置
            j--;
        ans[pro[i].id] = j - st[pro[i].id].size();
    }

但这样会很容易的被数据卡主,比如当所有的人的技能值都一样的时候。直接退化成一个 \(O(n^2)\) 的循环, \(2e^5\) 的数据范围肯定会炸。

改良方法

利用一个辅助数组,预处理最近一个比他小的位置。光用文字不是很好说,看一下代码然后手动模拟一遍应该就清楚了。

    int cnt=1,pre=1;
    Node now; // Node类型存的是人的信息
    for(int i=1;i<=n;++i){
        if(pro[i].r==now.r)
            cnt++;
        else{
            now = pro[i];
            cnt =1;
        }
        father[i] -=cnt;
    }

然后我们在循环中就可以直接使用这个数组了

    for(int i=n;i>=1;--i)
        ans[pro[i].id] = (i+father[i]) - st[pro[i].id].size();

这样这个循环就是一个稳定的 \(O(n)\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+7;
struct Node{
    int r,id;
    Node(){r=id=0;}
}pro[maxn];
set<int> st[maxn];
int ans[maxn]={0},father[maxn]={0};
bool cmp(Node a,Node b)
{
    if(a.r==b.r)
        return a.id<b.id;
    return a.r<b.r;
}
int main()
{
    //freopen("in.txt","r",stdin);
    ios::sync_with_stdio(false);
    int n,k,x,y;
    cin>>n>>k;
    for(int i=1;i<=n;++i){
        cin>>pro[i].r;
        pro[i].id=i;
    }
    for(int i=1;i<=k;++i){
        cin>>x>>y;
        if(pro[x].r>pro[y].r)
            st[x].insert(y);
        else if(pro[x].r<pro[y].r)
            st[y].insert(x);
    }
    /*for(int i=1;i<=n;++i){
        cout<<i<<" ";
        for(set<int>::iterator it=st[i].begin();it!=st[i].end();++it)
            cout<<*it<<" ";
        cout<<endl;
    }
    cout<<endl;*/
    sort(pro+1,pro+1+n,cmp);

    int cnt=1,pre=1;
    Node now;
    for(int i=1;i<=n;++i){
        if(pro[i].r==now.r)
            cnt++;
        else{
            now = pro[i];
            cnt =1;
        }
        father[i] -=cnt;
    }
    /*for(int i=1;i<=n;++i)
        cout<<father[i]<<" ";
    cout<<endl;*/
    for(int i=n;i>=1;--i){
        ans[pro[i].id] = (i+father[i]) - st[pro[i].id].size();
    }
    for(int i=1;i<=n;++i)
        cout<<ans[i]<<" ";
    cout<<endl;
    return 0;
}

G - Petya's Exams

题目大意:

\(n\) 场考试,一共 \(m\) 天。每一场考试都有三个参数:\(s\) 通知有这场考试的那一天, \(d\) 考试的那一天,\(c\) 复习所需要的天数

对于每一场考试,都必须复习足够的天数才能满足不挂科。

现在让你输出让所有考试都能顺利通过的时间安排,如果解不存在,输出 \(-1\)

数据保证每一场考试都不在同一天

(有SPJ,所以只要输出合法即可)

解题思路:

贪心的思想。

按每场考试结束的时间进行排序,然后从越早结束的考试开始,然后同考试通知的日期开始复习,直到天数足够。这样安排的过程中,如果出现日期冲突则说明解不存在。

(我是根据样例发现的规律,感觉是没毛病的。但我不会证明。)

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=110;
struct Node{
    int s,d,c,id;
}exam[maxn];
int ans[maxn]={0};
bool cmp(Node a,Node b)
{
    return a.d<b.d;
}
int main()
{
    //freopen("in.txt","r",stdin);
    ios::sync_with_stdio(false);
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;++i){
        cin>>exam[i].s>>exam[i].d>>exam[i].c;
        exam[i].id=i;
        ans[exam[i].d] = m+1;
    }
    sort(exam+1,exam+1+m,cmp);
    bool flag=false;
    for(int i=1;i<=m;++i){
        int now = exam[i].s,cnt=0;
        while(cnt<exam[i].c&&now<exam[i].d)
        {
            //cout<<now<<endl;
            if(ans[now]==0){
                ans[now] =exam[i].id;
                cnt++;
            }
            now ++ ;
        }
        if(cnt<exam[i].c){
            //cout<<exam[i].id<<endl;
            flag=true;
            break;
        }
    }
    if(flag)
        cout<<-1<<endl;
    else{
        for(int i=1;i<=n;++i)
            cout<<ans[i]<<" ";
    }
    return 0;
}

posted @ 2018-05-16 00:43  SCaryon  阅读(175)  评论(0编辑  收藏  举报