L.Simone and Graph Coloring (求最长下降子序列)
题目



给你一个从1到n的序列,如果存在逆序对<a[i], a[j]>,那么a[i], a[j]之间有一条边。这样,这些点能组成一张图。现在给这些点染色,要求相连的两个点之间的颜色不能相同。问你最小需要多少种颜色把图全部染完,且输出每个点需要染的颜色,染色编号从1到n。
只要有逆序对,就有边,就不能颜色相同。可以发现,对于a[i],它与前面比它大的点的颜色都不相同,且染色的编号是包含该点的最长下降子序列+1.
所以转求每个点的最长下降子序列。
有两种方法:
1、线段树求最长下降子序列
2、类比求最长上升子序列LIS,用DP+单调栈求最长下降子序列
线段树求最长下降子序列
线段树区间维护包含每个数的最长下降子序列数。
1、建树:区间初始化为0
2、更新:点goal的值为x
3、查询:查询区间[l, r]的最大值。即这个区间里的数的最大最长下降子序列数。
思路:
对于输入的每一个数a[i],都查询比它大的区间里的最大值MAX,这样保证找到的是最长下降子序列。那么对于数a[i],它的最长下降子序列数为MAX+1,把这个点更新掉即可。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll maxn = 1e6+5;
ll t, n,ans[maxn];
ll a[maxn], res;
ll cnt[maxn<<2];
void Build(ll l, ll r, ll pos){
if(l == r){cnt[pos] = 0; return; }
ll m = (l+r)>>1;
Build(l, m, pos<<1);
Build(m+1, r, pos<<1|1);
cnt[pos] = cnt[pos<<1] + cnt[pos<<1|1];
}
ll query(ll L, ll R, ll l, ll r, ll pos){
if(L <= l && r <= R){return cnt[pos]; }
ll m = (l+r)>>1;
ll MAX = 0;
if(L<=m) MAX = max(MAX,query(L, R, l, m, pos<<1));
if(R > m) MAX = max(MAX,query(L, R, m+1, r, pos<<1|1));
return MAX;
}
void update(ll goal, ll x, ll l, ll r, ll pos){
if(l == r){cnt[pos] = x; return; }
ll m = (l+r)>>1;
if(goal <= m) update(goal, x, l, m, pos<<1);
else update(goal, x, m+1, r, pos<<1|1);
cnt[pos] = max(cnt[pos<<1], cnt[pos<<1|1]);
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
cin >> t;
while(t--){
cin >> n;
Build(1, n, 1);
res = -1;
for(ll i = 1; i <= n; i++){
cin >> a[i];
ans[i] = query(a[i]+1, n, 1, n, 1)+1;
update(a[i], ans[i], 1, n, 1);
res = max(ans[i], res);
}
cout << res << endl;
for(ll i = 1; i <= n; i++){
cout << ans[i] << ((i!=n)?' ':'\n');
}
}
}
DP O(nlogn) 求最长下降子序列
都能看懂的LIS(最长上升子序列)问题_ltrbless的博客-CSDN博客_最长上升子序列
↑参考这篇博客,会发现mp[i]同时也记录了元素前面(含本元素)有多少个LIS。改一下即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
int t, n;
int mp[maxn], st[maxn];
int top, ans;
int a[maxn];
int main(){
ios::sync_with_stdio(false);cin.tie(0);
cin >> t;
while(t--){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
top = 0;
st[++top] = a[1];
mp[1] = 1;
for(int i = 2; i <= n; i++){
if(a[i] < st[top]){
st[++top] = a[i];
mp[i] = top;
}
else{
int l = lower_bound(st + 1, st + 1 + top, a[i], greater<int>()) - st;
st[l] = a[i];
mp[i] = l;
}
}
cout << top << endl;
for(int i = 1; i <= n; i++) cout << mp[i] << ((i!=n) ? ' ' : '\n');
}
}
wa点
这道题,如果用cin, cout,不开IO会tle
文章介绍了如何解决L.Simone and Graph Coloring问题,该问题涉及求解序列的最长下降子序列。通过建立线段树或者采用动态规划(DP)的方法,可以找到每个点的最长下降子序列,进而确定最小所需颜色数量。线段树方法用于区间维护最长下降子序列的长度,而DP方法则通过对序列进行O(nlogn)遍历来求解。注意,使用cin, cout可能导致超时,建议优化输入输出方式。"
82802368,7770087,深入理解Linux:文件权限管理-chgrp、chown与chmod,"['Linux指令', '文件权限管理']

3908

被折叠的 条评论
为什么被折叠?



