离散化+各种神奇科技

2020.9.08 更新 : 添加科技尺取法
2020.9.09 更新 : 添加科技折半枚举 顺便更新下二的代码

主要讲讲我这个蒟蒻的离散化做法(只讲大数据化小数据)

首先拷贝一份数据进r数组

接着对r数组排序手动去重塞进num数组里边

用的时候二分就好了

code:

/*
给定一个长度为n的序列和m个询问xm,对于每个询问xi输出xi在序列内的排名
(如果xi没有出现过则输出-1)
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 100010;
int a[N] , r[N] , num[N] , len;
int n , m;

int find(int x)
{
	int l = 1 , r = len , mid , best = -1;
	while(l <= r)
	{
		mid = (l + r) >> 1;
		if(num[mid] >= x)
		{
			best = mid;
			r = mid - 1;
		}
		else l = mid + 1;
	}
	if(num[best] == x) return best;
	else return -1;
}

int main()
{
	cin >> n;
	for(int i = 1 ; i <= n ; i++)
	{
		cin >> a[i];
		r[i] = a[i];
	}
	sort(r + 1 , r + n + 1);
	for(int i = 1 ; i <= n ;)
	{
		int j = i + 1;
		while(r[i] == r[j] && j <= n) j++;
		num[++len] = r[i] ; i = j;
	}
	cin >> m;
	for(int i = 1 , x ; i <= m ; i++)
	{
		cin >> x;
		cout << find(x) << endl;
	}
	return 0;
}

结合几道题用一用

一.dp

很明显的状态机转移dp,主要是法术类型ki跟xi范围有点大,于是我们对它们一块进行离散化

温馨提示:xi不一定是k数组里边的数

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long ll;
const int N = 500100 , M = 510;
//const ll inf = 1e7 + 1;
int n , m , x[N] , K[N] , len;
ll dp[3][M][3] , sum[N] , ans , rank[N] , P[N] , Maxn;
int num[N] , r[N];

inline int read()
{
	char c ; int res = 0;
	while(c < '0' || c > '9') c= getchar();
	while(c >= '0' && c <= '9')
	{
		res = res * 10 + c - '0';
		c = getchar();
	}
	return res;
}

inline void work()
{
	for(int i = 1 , j ; i <= n ;)
	{
		j = i + 1;
		while(r[j] == r[i] && j <= n) j++;
		num[++len] = r[i];
		i = j;
	}
}
inline int find(int x1)
{
	int l = 1 , r = len , mid , best = r;
	while(l <= r)
	{
		mid = (l + r) >> 1;
		if(num[mid] <= x1)
		{
			best = mid;
			l = mid + 1;
		}
		else r = mid - 1;
	}
	if(x1 == num[best]) return best;
	else return 0;
}

int main()
{
	n = read() ; m = read();
	memset(dp , -0x3f3f3f3f3f , sizeof dp);
	dp[0][0][0] = dp[0][1][0] = 0;
	for(int i = 1 ; i <= n ; i++)
	{
		K[i] = read();
		P[i] = read();
		r[i] = K[i];
	}
	for(int i = 1 ; i <= n ; i++)
		x[i] = read();
	sort(r + 1 , r + n + 1);
	work();
	for(int i = 1 ; i <= n ; i++)
	{
		Maxn = max(Maxn , P[i]);
		sum[find(K[i])] += P[i];
		int X = find(x[i]);
		dp[i & 1][0][0] = max(dp[(i + 1) & 1][0][0] + Maxn , dp[(i + 1) & 1][0][0] + sum[X]);//转移一 
		for(int k = 1 ; k <= m ; k++)
		{
			dp[(i & 1)][k][0] = max(dp[((i + 1) & 1)][k][1] + 2 * sum[X] , dp[((i + 1) & 1)][k][0] + sum[X]);//转移一 
			dp[(i & 1)][k][0] = max(dp[(i & 1)][k][0] , max(dp[((i + 1) & 1)][k][1] + 2 * Maxn , dp[((i + 1) & 1)][k][0] + Maxn));//转移二 
			dp[(i & 1)][k][1] = dp[((i + 1) & 1)][k - 1][0];//转移三  
		}//用了k次翻倍后 
	}
	printf("%lld" , dp[n & 1][m][0]);//这里有点玄学了
	//应该搜一遍dp[n & 1][1 到 m][0]跟dp[n & 1][1到 m][1]再取最大值的
	return 0;
}

实测504ms(优化全开),目前最优解第10名

二.并查集

单独处理xi == xj,再单独判断不相等的即可

还是注意离散化:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1000010;
struct nod
{
    int x , y , w;
} a[N];
int n , T;
int num[N] , len;
int p[N];

bool cmp(nod a , nod b)
{
    return a.w < b.w;
}
int Find(int x)
{
    return p[x] == x ? x : p[x] = Find(p[x]);
}
int find(int x)
{
    int l = 1 , r = len , mid , best;
    while(l <= r)
    {
        mid = (l + r) >> 1;
        if(num[mid] >= x)
        {
            best = mid;
            r = mid - 1;
        }
        else l = mid + 1;
    }
    return best;
}
bool check(int i)
{
    for(; i <= n ; i++)
        if(Find(a[i].x) == Find(a[i].y)) return false;
    return true;
}
int main()
{
    scanf("%d" , &T);
    while(T--)
    {
        len = 0;
        scanf("%d" , &n);
        for(int i = 1 ; i <= n ; i++)
        {
            scanf("%d%d%d" , &a[i].x , &a[i].y , &a[i].w);
            num[++len] = a[i].x , num[++len] = a[i].y;
            if(a[i].w == 0) a[i].w = 2;
        }
        sort(a + 1 , a + n + 1 , cmp);
        sort(num + 1 , num + 2 * n + 1);
        len = 0;
        for(int i = 1 , j ; i <= 2 * n ;)
        {
            j = i + 1;
            while(num[i] == num[j] && j <= 2 * n) j++;
            num[++len] = num[i];
            i = j;
        }
        for(int i = 1 ; i <= len ; i++) p[i] = i;
        for(int i = 1 ; i <= n ; i++) a[i].x = find(a[i].x) , a[i].y = find(a[i].y);
        int i = 1;
        for( ; a[i].w == 1; i++)
        {
            int x = Find(a[i].x) , y = Find(a[i].y);
            if(p[x] == p[y]) continue;
            else p[x] = p[y];
        }
        if(check(i)) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

三.尺取法

大意为求最小区间长度使得所有类型的元素均包含在内

按上面做法离散化类型,那总共类型个数即len

all数组储存某种元素的个数,变量now储存当前不同元素的个数

结合尺取法,若now < len 则向右拓展区间,并检查all,如果为空则now++,对应all++

若now == len 则检查左边界all,使其-1后若为0,则now–,左边界右移

code:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1000010;
int n;
int num[N] , len;//离散数组
int P[N] , c[N] , all[N] , ans;
int now;
int find(int x)
{
	int l = 1 , r = len , mid , best;
	while(l <= r)
	{
		mid = (l + r) >> 1;
		if(num[mid] >= x)
		{
			best = mid;
			r = mid - 1;
		}
		else l = mid + 1;
	}
	return best;
}

int main()
{
	scanf("%d" , &n);
	for(int i = 1 ; i <= n ; i++)
	{
		scanf("%d" , &P[i]);
		num[i] = P[i];
	}
	sort(num + 1 , num + n + 1);
	for(int i = 1 , j ; i <= n ;)
	{
		j = i + 1;
		while(num[i] == num[j] && j <= n) j++;
		num[++len] = num[i];
		i = j;
	}//后来我发现num跟r可以合一块写...
	ans = n;
	int l = 1 , r = 1;
	now = 1;
	all[find(P[1])]++;
	while(l <= n && r <= n)
	{
		if(now != len)
		{
			r++ ;
			if(!all[find(P[r])]) now++;
			all[find(P[r])]++;
		}
		else
		{
			ans = min(ans , r - l + 1);
			all[find(P[l])]--;
			if(!all[find(P[l])]) now--;
			l++;
		}
	}
	printf("%d" , ans);
	return 0;
} 

四.折半枚举

我们可以对数列一,二里的元素两两求和存到x数组里,数列三,四里的元素同理处理后存到y数组里

接下来对数组x和y离散化,同时将相同元素个数合并到离散化后的元素里,最后枚举x数组,在y数组里二分找到枚举值的相反数,利用乘法原理统计答案即可

算法复杂度为O(n²logn),可以接受(但不知道为啥洛谷那边T飞了)

code:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 5001 , M = 5001 * 5001;
struct nod
{
	int data , num;
} x[M] , y[M];
int n;
int a[N][5] , t , len1 , len2;
bool cmp(nod a , nod b)
{
	return a.data < b.data;
}
int find(int x)
{
	int l = 1 , r = len2 , mid , best = len2;
	while(l <= r)
	{
		mid = (l + r) >> 1;
		if(y[mid].data >= x)
		{
			best = mid;
			r = mid - 1;
		}
		else l = mid + 1;
	}
	if(y[best].data == x) return y[best].num;
	else return -1;
}
int main()
{
	scanf("%d" , &n);
	for(int i = 1 ; i <= n ; i++)
		for(int j = 1 ; j <= 4 ; j++)
			scanf("%d" , &a[i][j]);
	t = n * n;
	for(int i = 1 ; i <= n ; i++)
		for(int j = 1 ; j <= n ; j++)
			x[(i - 1) * n + j].data = a[i][1] + a[j][2] ,
			y[(i - 1) * n + j].data = a[i][3] + a[j][4] ;
	sort(x + 1 , x + t + 1 , cmp);
	sort(y + 1 , y + t + 1 , cmp);
	len1 = 0 , len2 = 0;
	for(int i = 1 , j ; i <= t ;)
	{
		j = i + 1;
		while(x[i].data == x[j].data && j <= t) j++;
		x[++len1].data = x[i].data;
		x[len1].num = j - i;
		i = j;
	}
	for(int i = 1 , j ; i <= t ; )
	{
		j = i + 1;
		while(y[i].data == y[j].data && j <= t) j++;
		y[++len2].data = y[i].data;
		y[len2].num = j - i;
		i = j;
	}
	int ans = 0;
	for(int i = 1 ; i <= len1 ; i++)
	{
		int k = find(-x[i].data);
		if(k > 0)
			ans += x[i].num * k;
	}
	printf("%d" , ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值