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;
}