比赛通过题数:5
感觉配合还是有点问题, 不应该让cb一个人开K,他前一个半小时思路错了,后来才发现, 卡A的时候,应该一个人静静想想,另一个人去帮cb开K的..总结教训..
A.Assigning Workstations && BZOJ 4425
佩内洛普是新建立的超级计算机的管理员中的一员。 她的工作是分配工作站给到这里来运行他们的计算研究任务的研究人员。
佩内洛普非常懒惰,不喜欢为到达的研究者们解锁机器。 她可以从在她的办公桌远程解锁这些机器,但她并不觉得这卑贱的任务配得上她,所以她决定忽略安全指南偷偷懒。她可以直接地要求,研究者在他们离开时不用锁定自己的工作站,然后把未在使用且还在未锁定状态的工作站分配给新来的研究人员。 这样,她只需要为每一个工作站第一次被使用所属的研究员解锁工作站,这对佩内洛普的工作来说是一个巨大的改善。
不幸的是,如果一个工作站在未锁定且没被使用的状态下超过m分钟,会自动锁定自己,佩内洛普必须为使用它的下一个研究员再次打开它。 鉴于抵达和离开的研究人员的确切时间表,你可以告诉佩内洛普,要求研究者在离开时不锁定工作站最多可以使她节约多少次的解锁工作。你可以认为这儿总是有足够的可用工作站。
Input
一行两个整数n (1≤n≤300000) 研究员的数量n,以及 m (1≤m≤100000000) 工作站在未锁定且没被使用的状态下超过m分钟会自动锁定。
下面的n行,每一行两个整数a与s (1≤a,s≤100000000) 表示一个研究员在第a分钟时到达以及待了s分钟后离开。
Output
输出研究者在离开时不锁定工作站最多可以使她节约多少次解锁工作。
思路:按照左端点排序, 对于每个新进来的人,他肯定要么自己一台机器,要么跟前面某个人一台机器, 如果能跟前面的,肯定贪心选结束时间最早且跟他相差m以内的,让m以内的其他点有可能跟后面的人匹配。
比赛的时候最开始猜了一个右端点排序, 对于每个左端点贪心找前面结束时间m以内,跟他相距最远的,这样是有问题的, 因为按照结束时间排序, 开始的时间就影响不了什么了, 这样排序之后,对于每个人 他后面的那个人 可能不是紧跟着他来的那个人, 再下一个可能才是紧跟着他来的人, 这样就导致了可能更新完了, 紧跟着那个人要重开一台机器,而答案是要紧跟着那个人跟他一台机器。
这样按照左端点排序,就是按照时间顺序决定每个人该干什么
#include <bits/stdc++.h>
using namespace std;
int n, m, ans;
priority_queue< int, vector< int >, greater< int > > q;
pair< int, int > p[300005];
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++)
scanf("%d%d", &p[i].first, &p[i].second);
sort(p, p + n);
for (int i = 0; i < n; i++)
{
while (!q.empty() && q.top() + m < p[i].first)
q.pop();
if (q.empty() || q.top() > p[i].first)
ans++;
else
q.pop();
q.push(p[i].first + p[i].second);
}
printf("%d\n", n - ans);
return 0;
}
Input
Output
我们就要推导下标啦。
总之就是要解这个方程
x、i、n为非负整数,已知n、i,求最小的x
ps : 下面这个式子是因为你随着分的块越多,i越大,那么这个i就是当前块, 最长区间为ceil(n/i) 。因为我们要跳过重复的,
所以要直接找到下一个块数 x.他们两个区间长度差1
有两种方法
第一种:看Claris博客,得到x = (n - 1) / ((n - 1) / i) + 1(这里的除均为整除)
第二种:
首先要知道
(具体数学里面有)
带入,得到
同乘上x
因为我们要求x的最小值,所以我们只看左边的不等式
因为x为非负整数,左边不一定是整数,那么x的最小值应该是
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
const ll INF = 1e18;
ll dp[maxn], r, p;
ll ceil(int a, int b)
{
return (a+b-1)/b;
}
ll dfs(int n)
{
if(n <= 1)
return 0;
if(dp[n] != -1)
return dp[n];
ll ans = (ll)(n-1)*p + r;
for(int i = 2; i < n; i = ceil(n, (ceil(n,i)-1)))
{
ans = min(ans, dfs(ceil(n, i)) + (ll)(i-1)*p+r);
}
return dp[n] = ans;
}
int main()
{
int n;
while(~scanf("%d%lld%lld", &n, &r, &p))
{
memset(dp, -1, sizeof(dp));
printf("%lld\n", dfs(n));
}
return 0;
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
const int maxn = 1e4+5;
struct node
{
ll x, y, v;
char sb;
}a[maxn];
int n;
vector<int> g[maxn];
int match[maxn], vis[maxn];
ll val[maxn];
bool dfs(int x)
{
int len = g[x].size();
for(int i = 0; i < len; i++)
{
int to = g[x][i];
if(!vis[to])
{
vis[to] = 1;
if(!match[to] || dfs(match[to]))
{
match[to] = x;
return 1;
}
}
}
return 0;
}
int Hungary()
{
int res = 0;
memset(match, 0, sizeof(match));
for(int i = 1; i <= n; i++)
{
memset(vis, 0, sizeof(vis));
res += dfs(i);
}
return res;
}
int main(void)
{
while(cin >> n)
{
int cnt = 1;
map<ll, int> m;
set<int> s;
s.clear(), m.clear();
for(int i = 1 ; i <= n; i++)
g[i].clear();
for(int i = 1; i <= n; i++)
{
scanf("%I64d%I64d", &a[i].x, &a[i].y);
ll add = a[i].x+a[i].y;
ll sub = a[i].x-a[i].y;
ll fac = a[i].x*a[i].y;
if(s.find(add) == s.end()) m[add] = cnt++, s.insert(add);
if(s.find(sub) == s.end()) m[sub] = cnt++, s.insert(sub);
if(s.find(fac) == s.end()) m[fac] = cnt++, s.insert(fac);
g[i].push_back(m[add]);
g[i].push_back(m[sub]);
g[i].push_back(m[fac]);
val[m[add]] = add;
val[m[sub]] = sub;
val[m[fac]] = fac;
}
int ans = Hungary();
// cout << ans << endl;
if(ans != n) puts("impossible");
else
{
for(int i = 1; i < cnt; i++)
if(match[i])
a[match[i]].v = val[i];
for(int i = 1; i <= n; i++)
{
if(a[i].v == a[i].x+a[i].y) a[i].sb = '+';
if(a[i].v == a[i].x-a[i].y) a[i].sb = '-';
if(a[i].v == a[i].x*a[i].y) a[i].sb = '*';
}
for(int i = 1; i <= n; i++)
printf("%I64d %c %I64d = %I64d\n", a[i].x, a[i].sb, a[i].y, a[i].v);
}
}
return 0;
}
答案 = 总对数 - 不满足条件的对数.
现在就是考虑怎么求不满足的对数.这里巧妙的利用了BIT
我们可以发现不满足偏序性质的这一对,一定是在某两个排列里位置是相同的,只有一个和他们的位置不相同,那么我们可以对每两个排列求一次不满足题意的偏序对的个数.但是这样算的话 对于那个排列里位置不同的数对,在另两个排列被算了两次,所以把最后的答案除以2就是最终的结果了.
求偏序对的话就是对于两个排列x,y. 先预处理处理x 中每一个x[i]的位置i,然后从后往前遍历 y[i].
那么关于一个点对x,y,若它们在两个排列中
____y__u______x____k
_______x___________y
这样从后向前扫第二个排列,扫到y的时候在第一个排列y的位置+1,然后扫到x的时候查询第一个排列x位置的前缀和,前缀和即为不满足条件的对数
所以就对扫到的每一个数都这样做就行了
即
将第二个排列中所有偏序对的位置固定下来作为一个标准,那么从后往前遍历第二个排列时,假设遍历到第i个,那么第i个后面所有的数都在他的后面了,由于我们把i后面的数都在第一个排列中的对应位置+1,那么现在我们求一下第二个排列中的第i个数在第一个排列中对应位置前面有多少个数,就可以求出不满足的偏序对个数了,好好体会..
get到了一种求两个序列之间的“逆序对”的方法
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 10;
const int Mod = 1e9 + 7;
int c[maxn], pos[maxn], A[maxn], B[maxn], C[maxn], n;
int lowbit(int x)
{
return x & -x;
}
void add(int x, int d)
{
while(x < maxn)
{
c[x] += d;
x += lowbit(x);
}
}
int sum(int x)
{
int ans = 0;
while(x)
{
ans += c[x];
x -= lowbit(x);
}
return ans;
}
ll solve(int *x, int *y)
{
for(int i = 1; i <= n; i++)
pos[x[i]] = i, c[i] = 0; //把第一个序列每个值所在的位置映射到pos上
ll ans = 0;
for(int i = n; i >= 1; i--)
{
ans += sum(pos[y[i]]); // 这都是在第二个序列后面的, 如果在第一个序列有在前面的,说明他们的相对序列不一样
add(pos[y[i]], 1);
}
return ans;
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &A[i]);
for(int i = 1; i <= n; i++) scanf("%d", &B[i]);
for(int i = 1; i <= n; i++) scanf("%d", &C[i]);
ll ans = (ll)n*(n-1)/2;
ans -= (solve(A,B) + solve(A, C) + solve(B,C)) / 2;
printf("%lld\n", ans);
return 0;
}