2018-7-7 ACM 刷题日记

从今天开始就正式回归刷题日常啦~

<Codeforces 1004C>

题意:

给一个序列,每个数都和它后面的所有数进行配对,问能组成多少个不同的有序对

思路:

先转化一下题意,就是找每个数后面有多少个不同的数。首先1e5的数据 n^2 显然不可行,那就用前缀处理一下。

首先进行如下操作:

for(int i = 2; i <= n; i++) {
		if(cnt[a[i]] == 0) tot++; //这个数没出现过, tot就++, tot记录第一个数后面有多少个不同的数
		cnt[a[i]]++; //cnt记录a[2] ~ a[n]每个数个出现了多少次
	}

这步操作可以得出第一个数后面有tot个不同的数,这就意味着第一个数可以和它后面的所有数组成tot个有序对,这就是前缀的初值,第一个数后面的不同的数的个数,即 tot,下面就向后推进,看第二个数被标记过与否,以及后面有多少不同的数,对tot进行更改在累加到ans上即可。那么接下来的问题就是如何对tot进行更改,当记前数为 a[i],如果a[i] 在未被标记的情况下不只出现一次,那就意味着a[i]后面,tot 个不同的值中一定有一个a[i],十七构成有序对 <a[i],  a[i]>,记下一个值为a[i + 1],如果a[i + 1]只出现一次,即cnt[a[i + 1]] == 1,那就说明不能构成有序对 <a[i + 1],  a[i + 1]>,那就比先前的tot少了一种,即tot--。不过只处理到这种程度程序并没有结束,还不能AC,举个栗子,1 2 2 3 4 ,对于a[2]这个2来说,cnt[a[2]] = 2,对于a[3]这个2来说,cnt[a[3]] = 2,但是执行到a[3]的时候,a[3]后面只有两个不同的数了,即3和4,而在a[2]这个时候,a[2]后面有三个不同的数,即2,3,4。那么怎么让进行到a[3]的时候,tot从3变成2呢,虽然标记过来了2这个数,到a[3]的时候不进行累加,但是tot的值却没改变,所以要解决这个问题,就在循环每次都执行:cnt[a[i + 1]]--;  即可,你可以理解为,这个值被利用过了,所以在后面继续枚举的时候,相当于它的个数减一,用过的就不往里算了。然后当她减到1的时候,就执行tot--,这样就处理了这个问题。

本人AC代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <cctype>
#include <ctime>
#include <stack>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int Inf = 1e9 + 7;
const int Maxx = 1e5 + 7;
typedef pair <int, int> pr;
priority_queue <pr> qua;
set <int> sst;
map <int, int> cnt;
map <int, bool> vis;
vector <int> vect;
int n;
int a[Maxx];
ll ans;

int main() {
	cnt.clear();
	vis.clear();
	cin >> n;
	for(int i = 1; i <= n; i++) cin >> a[i];
	ll tot = 0;
	for(int i = 2; i <= n; i++) {
		if(cnt[a[i]] == 0) tot++; //这个数没出现过, tot就++, tot记录第一个数后面有多少个不同的数, 前缀初值
		cnt[a[i]]++; //cnt记录a[2] ~ a[n]每个数各出现了多少次
	}
	//cnt[a[1]]++;
	for(int i = 1; i < n; i++) {
		if(!vis[a[i]]) ans += tot, vis[a[i]] = 1;
		//cout << i << "->" << tot << endl;
		if(cnt[a[i + 1]] == 1) tot--;
		cnt[a[i + 1]]--;
	}
	cout << ans << endl;
}

猜你喜欢

转载自blog.csdn.net/EricGipsy/article/details/80951074