題目大意:一個序列,讓你求出最大上升子序列是多少,然後有多少個最長上升子序列(即每一個元素都不相同)
思路:用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));
}
}