题目大意:一个序列,让你求出最大上升子序列是多少,然后有多少个最长上升子序列(即每一个元素都不相同)
思路:用nlogn的方法求出最长上升子序列.
这边讲一下网络流的建图模型:
添加超级源点和超级汇点.
每一个序列长度为1的元素与源点容量为1;
并且长度为x与x-1的元素有一条容量为1的边.表示可以属于同一个序列.
这边我是用vector存下每个长度的元素编号,下次直接拿出来建边就可以了.
长度为最大的元素与汇点有一条容量为1的边.
跑一边网络流就可以了.
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
#define MAXN 100010
#define MAXM 1000001
#define INF 0x7ffffff
int head[MAXN], dep[MAXN], gap[MAXN], cur[MAXN];
int a[MAXN];
int f[MAXN];
vector<int>ff[MAXN];
int cnt;
struct edge
{
int v;
int cap;
int flow;
int next;
}edg[MAXM];
void init()
{
cnt = 0;
memset(head, -1, sizeof(head));
}
void addedge(int u, int v, int w, int rw = 0)
{
edg[cnt].v = v;
edg[cnt].cap = w;
edg[cnt].flow = 0;
edg[cnt].next = head[u];
head[u] = cnt++;
edg[cnt].v = u;
edg[cnt].cap = rw;
edg[cnt].flow = 0;
edg[cnt].next = head[v];
head[v] = cnt++;
}
int Q[MAXN];
void BFS(int start, int end)
{
memset(dep, -1, sizeof(dep));
memset(gap, 0, sizeof(gap));
gap[0] = 1;
int front = 0, rear = 0;
dep[end] = 0;
Q[rear++] = end;
while (front != rear)
{
int u = Q[front++];
for (int i = head[u]; i != -1; i = edg[i].next)
{
int v = edg[i].v;
if (dep[v] != -1)continue;
Q[rear++] = v;
dep[v] = dep[u] + 1;
gap[dep[v]]++;
}
}
}
int S[MAXN];
int sap(int start, int end, int n)
{
BFS(start, end);
memcpy(cur, head, sizeof(head));
int top = 0;
int u = start;
int ans = 0;
while (dep[start] < n)
{
if (u == end)
{
int Min = INF;
int inser;
for (int i = 0; i < top; i++)
if (Min>edg[S[i]].cap - edg[S[i]].flow)
{
Min = edg[S[i]].cap - edg[S[i]].flow;
inser = i;
}
for (int i = 0; i < top; i++)
{
edg[S[i]].flow += Min;
edg[S[i] ^ 1].flow -= Min;
}
ans += Min;
top = inser;
u = edg[S[top] ^ 1].v;
continue;
}
bool flag = false;
int v;
for (int i = cur[u]; i != -1; i = edg[i].next)
{
v = edg[i].v;
if (edg[i].cap - edg[i].flow&&dep[v] + 1 == dep[u])
{
flag = true;
cur[u] = i;
break;
}
}
if (flag)
{
S[top++] = cur[u];
u = v;
continue;
}
int Min = n;
for (int i = head[u]; i != -1; i = edg[i].next)
if (edg[i].cap - edg[i].flow&&dep[edg[i].v] < Min)
{
Min = dep[edg[i].v];
cur[u] = i;
}
gap[dep[u]]--;
if (!gap[dep[u]])return ans;
dep[u] = Min + 1;
gap[dep[u]]++;
if (u != start)u = edg[S[--top] ^ 1].v;
}
return ans;
}
int Scan()//读入外挂
{
int res = 0, ch, flag = 0;
if ((ch = getchar()) == '-') //判断正负
flag = 1;
else if (ch >= '0' && ch <= '9') //得到完整的数
res = ch - '0';
while ((ch = getchar()) >= '0' && ch <= '9')
res = res * 10 + ch - '0';
return flag ? -res : res;
}
int find(int *a, int len, int n)//修改后的二分查找,若返回值为x,则a[x]>=n
{
int left = 0, right = len, mid = (left + right) / 2;
while (left <= right)
{
if (n>a[mid]) left = mid + 1;
else if (n<a[mid]) right = mid - 1;
else return mid;
mid = (left + right) / 2;
}
return left;
}
int main()
{
int n;
while (~scanf("%d", &n))
{
a[0] =-0x7ffffff;
int S = 0;
int E = n + 1;
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
ff[i].clear();
}
int cur = 1;
f[1] = a[1];
init();
int cnt2 = 2;
addedge(S, 1, 1);
int maxf = 1;
ff[0].push_back(0);
ff[1].push_back(1);
for (int i = 2; i <= n; i++)
{
int k = find(f, cur, a[i]);
f[k] = a[i];
ff[k].push_back(i);
maxf = max(maxf, k);
cur = max(cur, k);
k--;
for (int j = 0; j < ff[k].size(); j++)
{
addedge(ff[k][j], i, 1);
}
}
for (int i = 0; i < ff[maxf].size();i++)
addedge(ff[maxf][i], E, 1);
printf("%d\n", maxf);
printf("%d\n", sap(S, E, E));
}
}