[权值线段树] 1163B2 Cat Party (Hard Edition)

题意: 给一序列串求最大x 有 x前删除一个值后每个值出现次数相同

 解法: B1是小范围版, 只要枚举数字就行

B2用权值线段树记录最大最小出现次数, 出现n次的数有几个, 结合B1的规律(在代码里↓)写即可 

代码

/*
    Zeolim - An AC a day keeps the bug away
*/

//pragma GCC optimize(2)
#include <cstdio>
#include <iostream>
#include <bitset>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <string>
#include <cstring>
#include <algorithm>
#include <stack>
#include <queue>
#include <set>
#include <sstream>
#include <map>
#include <ctime>
#include <vector>
#include <fstream>
#include <list>
#include <iomanip>
#include <numeric>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#define max(a, b) a > b ? a : b
#define min(a, b) a > b ? b : a
using namespace std;
//using namespace __gnu_pbds;
//typedef tree <long long, null_type, less <long long>, rb_tree_tag, tree_order_statistics_node_update> rbtree;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const ld PI = acos(-1.0);
const ld E = exp(1.0);
const int INF = 0x3f3f3f3f;
const int MAXN = 1e6 + 10;
const ll MOD = 1e9 + 7;


int arr[MAXN] = {0}, cnt[MAXN] = {0}, rnt[MAXN] = {0}, num;

struct node
{
    int fst, lst, val, mival;
}sgtree[MAXN * 4];

void build(int now, int fst, int lst)
{
    sgtree[now].fst = fst, sgtree[now].lst = lst;
    if(fst == lst) { sgtree[now].val = 0, sgtree[now].mival = INF; return ; }
    int mid = (fst + lst) / 2;
    build(now * 2, fst, mid);
    build(now * 2 + 1, mid + 1, lst);
    sgtree[now].val = max(sgtree[now * 2].val, sgtree[now * 2 + 1].val);
    sgtree[now].mival = min(sgtree[now * 2].mival, sgtree[now * 2 + 1].mival);
}

int ask(int now, int fst, int lst)
{
    if(fst <= sgtree[now].fst && lst >= sgtree[now].lst) return sgtree[now].val;
    int mid = (sgtree[now].fst + sgtree[now].lst) / 2;
    int rval = -(1 << 30);
    if(fst <= mid)  rval = max(rval, ask(now * 2, fst, lst));
    if(lst > mid) rval = max(rval, ask(now * 2 + 1, fst, lst));
    return rval;
}

int askmi(int now, int fst, int lst)
{
    if(fst <= sgtree[now].fst && lst >= sgtree[now].lst) return sgtree[now].mival;
    int mid = (sgtree[now].fst + sgtree[now].lst) / 2;
    int rval = INF;
    if(fst <= mid)  rval = min(rval, askmi(now * 2, fst, lst));
    if(lst > mid) rval = min(rval, askmi(now * 2 + 1, fst, lst));
    return rval;
}

void change(int now, int pos, int rval)
{
    if(sgtree[now].fst == sgtree[now].lst) { sgtree[now].val = sgtree[now].mival = rval; return; }
    int mid = (sgtree[now].fst + sgtree[now].lst) / 2;
    if(pos <= mid) change(now * 2, pos, rval);
    else change(now * 2 + 1, pos, rval);
    sgtree[now].val = max(sgtree[now * 2].val, sgtree[now * 2 + 1].val);
    sgtree[now].mival = min(sgtree[now * 2].mival, sgtree[now * 2 + 1].mival);
}

int main()
{
    //ios::sync_with_stdio(false);
    //cin.tie(0);     cout.tie(0);
    //freopen("D://test.in", "r", stdin);
    //freopen("D://test.out", "w", stdout);
    
    int n, len;

    cin >> n;

    for(int i = 1; i <= n; ++i)
        cin >> arr[i], len = max(len, arr[i]);
    
    build(1, 1, len);
    
    int ans = 0;
    
    //cnt记录 x出现的次数 rnt记录出现 x次的数字有几个 num记录当前一共出现数字个数

    for(int i = 1; i <= n; ++i)
    {
    	int x = arr[i];
		
        if(!cnt[x]) ++num;
		
        ++cnt[x], ++rnt[cnt[x]];
		
	    if(i != 1)
		    --rnt[cnt[x] - 1];
		
        change(1, arr[i], cnt[arr[i]]);

        int mx = ask(1, 1, len), mi = askmi(1, 1, len); //求最大最小值
        
        if(mx == mi && rnt[mx] == 1)  ans = i; //就一个数
        
        else if(mx == mi && mi == 1)  ans = i; //一堆数但是出现次数都是1
        
        else if(mi == 1 && rnt[1] == 1 && rnt[mx] == num - 1) ans = i; //只有一个数出现一次 
        
        else if(mx - mi == 1 && rnt[mx] == 1) ans = i;//最多次比其他都多1
	}
	
	cout << ans << '\n';
        
    
    return 0;
}

 

posted @ 2019-05-10 21:29  张浦  阅读(136)  评论(0编辑  收藏  举报