补下 F H K 吧。
题目连接 :https://codeforces.com/gym/100269
F. Flight Boarding Optimization
题意 : 给你一个序列,要求把他按数字的大小分成 k 块,比如数字大小1~ 4为一块,5~12为一块,每块在原序列的顺序对( i<j && a[i]<a[j] )的数量相加最小。
题解 : 我们可以用树状数组预处理出每个区间,他所能构成的顺序对数量。再用动态规划求解。
dp[i][j]定义为在数字大小为 i 时 切了 j 块
转移方程为 :dp[i][j] = min(dp[i][j], dp[q][j - 1] + num[q+1][i])(q从1到 i,num是区间的顺序对,从切了j-1转移过来)
AC代码:
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include <string>
#include <cmath>
#include <ctime>
#include <queue>
#include<stack>
#include <map>
#include <set>
#include<fstream>
#define lowbit(x) x&(-x)
using namespace std;
typedef long long ll;
const int N = 1e3 + 5;
int a[N],c[N],num[N][N],dp[N][55];
vector<int>V[N];
int n, s, k;
void add(int i)
{
while (i <= n) {
c[i] ++;
i += lowbit(i);
}
}
int sum(int i)
{
int ans = 0;
while (i > 0) {
ans += c[i];
i -= lowbit(i);
}
return ans;
}
int main()
{
freopen("flight.in", "r", stdin);
freopen("flight.out", "w", stdout);
memset(num, 0, sizeof(num));
scanf("%d%d%d",&n,&s,&k);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
V[a[i]].push_back(i);
}
for (int i = 1; i <= s; i++) {//树状数组预处理好每个区间的顺序对数量
memset(c,0,sizeof(c));
for (int j = i; j <= s; j++) {
num[i][j] = num[i][j - 1];
for (int k = 0; k < V[j].size(); k++) {
num[i][j] += sum(V[j][k]);
}
for (int k = 0; k < V[j].size(); k++) {
add(V[j][k]);
}
}
}
memset(dp, 0x3f, sizeof(dp));
for (int i = 1; i <= s; i++)//初始化dp,切一次便是从1到 i 的顺序对了
dp[i][1] = num[1][i];
for (int i = 1; i <=s; i++) {
for (int j = 2; j <k; j++) {
for (int q =1; q <=i; q++) {
dp[i][j] = min(dp[i][j], dp[q][j - 1] + num[q+1][i]);//转移方程
}
}
}
int ans =num[1][s];// k可能等于0,所以ans赋值为k等于0的情况,而且切越多会越小,所以也是最大值。
for (int i = 1; i < s; i++) {//找切了k-1,也就是k块,再加上剩下的一块。
ans = min(ans, dp[i][k - 1] + num[i+1][s]);
}
printf("%d\n", ans);
return 0;
}
K - Kids in a Friendly Class
题意: 每个女生认识a个女生,b个男生,每个男生认识c个女生,d个男生,认识的人连边,问男生女生最少为多少,构成的图输出。
题解: 设女生有x人,男生有y人,可以得y=bx/c。所以x从c,y从b开始(题意至少得有这么多人)。bx=cy,所以b*(x+c/gcd(b,c))=c(y+b/gcd(b,c)),所以x,y每次加上对应得数后,看是否满足y>d && x>a &&!((x & 1) && (a & 1) || (y & 1) && (d & 1))(我们知道,每个点度数相加后%2=0,这些点才能连成一个图)
现在就是把图输出了,这时候就用到 Havel-Hakimi 定理,简单得说就是,度数先从大到小排序,度数最大(du)的点取出,与接下来的du个点连边,du这个点就不再用了,连边的点度数减一,若出现度数为负,则不能构成一个图。实现时可以变化一下,用优先队列维护度数最大,每次用度数最大与次大连边,次大减一放回队列,再取出次大,这样就行了。
AC代码:
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include <string>
#include <cmath>
#include <ctime>
#include <queue>
#include<stack>
#include <map>
#include <set>
#include<fstream>
#define lowbit(x) x&(-x)
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
const int INF = 0x3f3f3f3f;
int a, b, c, d;
int gcd(int a, int b)
{
return b ? gcd(b, a%b) : a;
}
void solve(int n, int m)
{
priority_queue<pair<int, int> >girl;
priority_queue<pair<int, int> >boy;
for (int i = 1; i <= n; i++)
girl.push(make_pair(a, i));
while (!girl.empty())
{
pair<int, int> now = girl.top();
girl.pop();
for (int i = 0; i < now.first; i++)
{
pair<int, int> next = girl.top();
girl.pop();
printf("%d %d\n", now.second, next.second);
next.first--;
girl.push(next);
}
}
for (int i = n + 1; i <= n + m; i++)
boy.push(make_pair(d, i));
while (!boy.empty())
{
pair<int, int> now = boy.top();
boy.pop();
for (int i = 0; i < now.first; i++)
{
pair<int, int> next = boy.top();
boy.pop();
printf("%d %d\n", now.second, next.second);
next.first--;
boy.push(next);
}
}
for (int i = 1; i <= n; i++)
girl.push(make_pair(b, i));
for (int i = n + 1; i <= m + n; i++)
boy.push(make_pair(c, i));
while (!girl.empty())
{
pair<int, int> now = girl.top();
girl.pop();
for (int i = 0; i < now.first; i++)
{
pair<int, int> next = boy.top();
boy.pop();
printf("%d %d\n", now.second, next.second);
next.first--;
if (next.first != 0)boy.push(next);
}
}
}
int main()
{
freopen("kids.in", "r", stdin);
freopen("kids.out", "w", stdout);
scanf("%d%d%d%d", &a, &b, &c, &d);
int g = gcd(b, c);
int dx = c / g, dy = b / g;
int x,y;
for (y = b, x = c; ; y += dy, x+=dx) {
if(y>d && x>a &&!((x & 1) && (a & 1) || (y & 1) && (d & 1)))
break;
}
printf("%d %d\n",x,y);
solve(x,y);
}
H 待补