A. Brick Wall
用横着的砖填满格子
#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 1e6 + 10;
const int INF = 1e16;
const ll mod = 1e9 + 7;
const int base = 13331;
void solve()
{
int n, m;
cin >> n >> m;
cout << m / 2 * n << '\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--)
solve();
return 0;
}
B. Minimize Inversions
这题要求 A A A数组和 B B B数组中的逆序对个数之和最少
我们假设 A A A数组有序,此时调整 A A A数组中的元素,若交换 a i a_i ai和 a j a_j aj,则会多出 j − i j - i j−i个逆序对,是最不优情况
所以当一个数组有序的时候即为答案
#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 1e6 + 10;
const int INF = 1e16;
const ll mod = 1e9 + 7;
const int base = 13331;
struct sj
{
int a, b;
} x[N];
bool cmp(sj x, sj y)
{
return x.a < y.a;
}
void solve()
{
int n;
cin >> n;
for(int i = 1;i <= n;i++)
cin >> x[i].a;
for(int i = 1;i <= n;i++)
cin >> x[i].b;
sort(x+1,x+n+1,cmp);
for(int i = 1;i <= n;i++)
cout << x[i].a << " ";
cout << '\n';
for(int i = 1;i <= n;i++)
cout << x[i].b << " ";
cout << '\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--)
solve();
return 0;
}
C. XOR-distance
我们使小的数变大,大的数变小,但是小的数不能大于大的数
记小的数为 a a a,大的数为 b b b,如果当前位 a a a为 0 0 0且 b b b为 1 1 1,通过异或操作,可以使得前位 a a a为 1 1 1且 b b b为 0 0 0,从最高位到最低位依次操作
注意,每次操作要考虑是否超过范围 r r r
#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 1e6 + 10;
const int INF = 1e16;
const ll mod = 1e9 + 7;
const int base = 13331;
void solve()
{
int a, b, r;
cin >> a >> b >> r;
int rr = r, cnt = 0, sum = 0;
while(rr > 0)
{
rr /= 2;
cnt++;
}
for(int i = cnt;i >= 0;i--)
{
if(a > b)
swap(a, b);
int aa = (a ^ (1LL << i)), bb = (b ^ (1LL << i));
if(((1LL << i) & a) == 0 && ((1LL << i) & b) > 0 && aa < bb)
{
if(sum + (1LL << i) <= r)
{
sum += (1LL << i);
a = (a ^ (1LL << i));
b = (b ^ (1LL << i));
}
}
}
cout << abs(a - b) << '\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--)
solve();
return 0;
}
D. Blocking Elements
二分答案,看选中的数之和是否超过 m i d mid mid
d p i dp_i dpi为最后一个选中的数为 a i a_i ai时的最小代价,所以我们只需要判断 d p n + 1 dp_{n+1} dpn+1和 m i d mid mid的大小关系
如果当前选中的位置为 i i i,由于要满足每段之和小于等于 m i d mid mid,所以我们可以得到满足条件的最前面的选点 j j j,可以通过双指针处理出 j j j
所以 d p i dp_i dpi的转移方程为 d p i = m a x ( m i n ( d p [ k ] ) + a [ i ] ) , ( j ⩽ k ⩽ i − 1 ) dp_i = max(min(dp[k]) +a[i]),(j \leqslant k \leqslant i - 1) dpi=max(min(dp[k])+a[i]),(j⩽k⩽i−1)
为了使时间复杂度满足题目要求,我们可以用双端队列优化,总时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 1e6 + 10;
const int INF = 1e16;
const ll mod = 1e9 + 7;
const int base = 13331;
void solve()
{
int n;
cin >> n;
vector<int> a(n+5);
for(int i = 1;i <= n;i++)
cin >> a[i];
int l = 1, r = 1e16;
while(l <= r)
{
deque<int> q;
q.push_back(0);
int mid = (l + r) / 2, cnt = 0;
vector<int> dp(n+5);
for(int i = 1, j = 0;i <= n + 1;i++)
{
while(cnt > mid)
cnt -= a[++j];
while(!q.empty() && q.front() < j) q.pop_front();
dp[i] = dp[q.front()] + a[i];
while(!q.empty() && dp[q.back()] >= dp[i]) q.pop_back();
cnt += a[i];
q.push_back(i);
}
if(dp[n + 1] > mid)
l = mid + 1;
else
r = mid - 1;
}
cout << l << '\n';
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--)
solve();
return 0;
}