暑假集训系列题解(七)
暑假集训系列题解(七)¶
题目1¶
题目链接¶
题目描述¶
Farmer John has a brilliant idea for the next great spectator sport: Cow Steeplechase! As everyone knows, regular steeplechase involves a group of horses that race around a course filled with obstacles they must jump over.FJ figures the same contest should work with highly-trained cows, as long as the obstacles are made short enough.
In order to design his course, FJ makes a diagram of all the N (1 <= N <=250) possible obstacles he could potentially build. Each one is represented by a line segment in the 2D plane that is parallel to the horizontal or vertical axis. Obstacle i has distinct endpoints (X1_i, Y1_i) and (X2_i, Y2_i) (1 <= X1_i, Y1_i, X2_i, Y2_i <= 1,000,000,000). An example is as follows:
FJ would like to build as many of these obstacles as possible, subject to the constraint that no two of them intersect. Starting with the diagram above, FJ can build 7 obstacles:
Two segments are said to intersect if they share any point in common, even an endpoint of one or both of the segments. FJ is certain that no two horizontal segments in the original input diagram will intersect, and that similarly no two vertical segments in the input diagram will intersect.Please help FJ determine the maximum number of obstacles he can build.
输入¶
- Line 1: A single integer: N.
- Lines 2..N+1: Line i+1 contains four space-separated integers representing an obstacle: X1_i, Y1_i, X2_i, and Y2_i.
输出¶
- Line 1: The maximum number of non-crossing segments FJ can choose.
样例输入¶
样例输出¶
提示¶
There are three potential obstacles. The first is a horizontal segment connecting (4, 5) to (10, 5); the second and third are vertical segments connecting (6, 2) to (6, 12) and (8, 3) to (8, 5).The optimal solution is to choose both vertical segments.
题解¶
网络流算法,将横向直线标号并与源点建边,并赋权值1;将纵向直线标号与汇点建边,赋权值1;将横纵交叉的直线建边,赋权值为正无穷;由最大流等于最小割,所以根据EK算法或者Dinic算法算出最大流即可。
代码¶
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
struct node
{
ll to, val, next;
};
node edge[100500] = {0};
ll head[4050] = {0};
ll cnt = 0;
ll s = 1, e = 1;
void add_edge(ll from, ll to, ll val)
{
edge[cnt].next = head[from];
edge[cnt].to = to;
edge[cnt].val = val;
head[from] = cnt++;
}
ll pre[4050] = {0}, tag[4050] = {0}, vis[4050] = {0};
ll bfs()
{
queue<ll> que;
memset(tag, 0, sizeof(tag));
memset(pre, 0, sizeof(pre));
memset(vis, 0, sizeof(vis));
que.push(s);
vis[s] = 1;
while (!que.empty())
{
ll now = que.front();
que.pop();
for (ll i = head[now]; i != -1; i = edge[i].next)
{
ll to = edge[i].to, val = edge[i].val;
if (!vis[to] && val > 0)
{
vis[to] = 1;
pre[to] = now;
tag[to] = i;
if (to == e)
return 1;
que.push(to);
}
}
}
return 0;
}
ll EK()
{
ll ans = 0;
while (bfs())
{
ll min1 = inf;
for (ll i = e; i != s; i = pre[i])
{
min1 = min(min1, edge[tag[i]].val);
}
for (ll i = e; i != s; i = pre[i])
{
edge[tag[i]].val -= min1;
edge[tag[i] ^ 1].val += min1;
}
ans += min1;
}
return ans;
}
struct node1
{
ll x1, y1, x2, y2, num;
};
node1 a[4050] = {0}, b[4050] = {0};
ll cnta = 0, cntb = 0;
int main()
{
//freopen("in.txt", "r", stdin);
ll n;
scanf("%lld", &n);
ll x1, x2, y1, y2;
for (ll i = 1; i <= n; i++)
{
scanf("%lld%lld%lld%lld", &x1, &y1, &x2, &y2);
if (x1 > x2)
swap(x1, x2);
if (y1 > y2)
swap(y1, y2);
if (x1 == x2)
a[++cnta] = {x1, y1, x2, y2, i};
else
b[++cntb] = {x1, y1, x2, y2, i};
}
for (ll i = 0; i < 4050; i++)
head[i] = -1;
e = 4000, s = 0;
for (ll i = 1; i <= cnta; i++)
{
add_edge(s, a[i].num, 1);
add_edge(a[i].num, s, 0);
//cout << s << ' ' << a[i].num << ' ' << 1 << endl;
}
for (ll i = 1; i <= cntb; i++)
{
add_edge(e, b[i].num, 0);
add_edge(b[i].num, e, 1);
//cout << e << ' ' << b[i].num << ' ' << 1 << endl;
}
for (ll i = 1; i <= cnta; i++)
{
for (ll j = 1; j <= cntb; j++)
{
if ((b[j].x1 <= a[i].x1 && a[i].x1 <= b[j].x2) && (a[i].y1 <= b[j].y1 && b[j].y1 <= a[i].y2))
{
add_edge(a[i].num, b[j].num, inf);
add_edge(b[j].num, a[i].num, 0);
//cout << a[i].num << ' ' << b[j].num << ' ' << inf << endl;
}
}
}
printf("%lld\n", n - EK());
}
该题另外一种做法为二分图,代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int> v[1005];
map<int, int> mp, vis, mp1;
int dfs(int now)
{
for (int i = 0; i < v[now].size(); i++)
{
int to = v[now][i];
if (!vis[to])
{
vis[to] = 1;
if (mp[to] == 0 || dfs(mp[to]))
{
mp[to] = now;
mp1[now] = to;
return 1;
}
}
}
return 0;
}
struct node1
{
ll x1, y1, x2, y2, num;
};
node1 a[4050] = {0}, b[4050] = {0};
ll cnta = 0, cntb = 0;
int main()
{
ll n;
scanf("%lld", &n);
ll x1, x2, y1, y2;
for (ll i = 1; i <= n; i++)
{
scanf("%lld%lld%lld%lld", &x1, &y1, &x2, &y2);
if (x1 > x2)
swap(x1, x2);
if (y1 > y2)
swap(y1, y2);
if (x1 == x2)
{
cnta++;
a[cnta] = {x1, y1, x2, y2, cnta};
}
else
{
cntb++;
b[cntb] = {x1, y1, x2, y2, cntb};
}
}
for (ll i = 1; i <= cnta; i++)
{
for (ll j = 1; j <= cntb; j++)
{
if ((b[j].x1 <= a[i].x1 && a[i].x1 <= b[j].x2) && (a[i].y1 <= b[j].y1 && b[j].y1 <= a[i].y2))
{
v[a[i].num].push_back(b[j].num);
}
}
}
int ans = 0;
for (int i = 1; i <= cnta; i++)
{
vis.clear();
ans += dfs(i);
}
cout << n-ans << endl;
}
题目2¶
题目链接¶
题目描述¶
Farmer John has lined up his N (1 <= N <= 100,000) cows in a row to measure their heights; cow i has height H_i (1 <= H_i <= 1,000,000,000) nanometers--FJ believes in precise measurements! He wants to take a picture of some contiguous subsequence of the cows to submit to a bovine photography contest at the county fair.
The fair has a very strange rule about all submitted photos: a photograph is only valid to submit if it depicts a group of cows whose median height is at least a certain threshold X (1 <= X <= 1,000,000,000).
For purposes of this problem, we define the median of an array A[0...K] to be A[ceiling(K/2)] after A is sorted, where ceiling(K/2) gives K/2 rounded up to the nearest integer (or K/2 itself, it K/2 is an integer to begin with). For example the median of {7, 3, 2, 6} is 6, and the median of {5, 4, 8} is 5.
Please help FJ count the number of different contiguous subsequences of his cows that he could potentially submit to the photography contest.
输入¶
* Line 1: Two space-separated integers: N and X. * Lines 2..N+1: Line i+1 contains the single integer H_i.
输出¶
* Line 1: The number of subsequences of FJ's cows that have median at least X. Note this may not fit into a 32-bit integer.
样例输入¶
样例输出¶
提示¶
FJ's four cows have heights 10, 5, 6, 2. We want to know how many contiguous subsequences have median at least 6.There are 10 possible contiguous subsequences to consider. Of these, only 7 have median at least 6. They are {10}, {6}, {10, 5}, {5, 6}, {6, 2}, {10,5, 6}, {10, 5, 6, 2}.
题解¶
sum数组记录求出大于k的数字个数的前缀和,则题目可以转变为寻找区间[l,r]满足sum[r]-sum[l-1]>=0,利用树状数组查找即可。
代码¶
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll tree[300500] = {0};
ll maxn = 300500;
ll lowbit(ll n)
{
return n & (-n);
}
void add(ll pos, ll val)
{
for (ll i = pos; i <= maxn; i += lowbit(i))
tree[i] += val;
}
ll query(ll pos)
{
ll ans = 0;
for (ll i = pos; i > 0; i -= lowbit(i))
ans += tree[i];
return ans;
}
int main()
{
ll n, m, k, sum = 0, ans = 0;
scanf("%lld%lld", &n, &k)
add(n,1); ///为防止负数出现,统一加n,这里初始化增添一个0
for (ll i = 1; i <= n; i++)
{
scanf("%lld", &m);
if (m < k)
sum -= 1;
else
sum += 1;
ans += query(sum + n);
add(sum + n, 1);
}
cout << ans << endl;
}
题目3¶
题目链接¶
题目描述¶
Eddy likes to walk around. Especially, he likes to walk in a loop called "Infinite loop". But, actually, it's just a loop with finite length(Anyway, the name doesn't matter). Eddy can walk in a fixed length. He finds that it takes him N steps to walk through the loop a cycle. Then, he puts N marks on the "Infinite loop" labeled with 0,1,…,N−1, where i and i+1 are a step away each other, so as 0 and N-1. After that, Eddy stands on the mark labeled 0 and start walking around. For each step, Eddy will independently uniformly randomly choose to move forward or backward. If currently Eddy is on the mark labeled i, he will on the mark labeled i+1 if move forward or i-1 if move backward. If Eddy is on the mark labeled N-1 and moves forward, he will stand on the mark labeled 0. If Eddy is on the mark labeled 0 and moves backward, he will stand on the mark labeled N-1.
Although, Eddy likes to walk around. He will get bored after he reaches each mark at least once. After that, Eddy will pick up all the marks, go back to work and stop walking around.
You, somehow, notice the weird convention Eddy is doing. And, you record T scenarios that Eddy walks around. For i-th scenario, you record two numbers Ni, Mi, where Ni tells that in the i-th scenario, Eddy can walk through the loop a cycle in exactly Ni steps(Yes! Eddy can walk in different fixed length for different day.). While Mi tells that you found that in the i-th scenario, after Eddy stands on the mark labeled Mi, he reached all the marks.
However, when you review your records, you are not sure whether the data is correct or even possible. Thus, you want to know the probability that those scenarios will happen. Precisely, you are going to compute the probability that first i scenarios will happen sequentially for each i.
输入¶
The first line of input contains an integers T. Following T lines each contains two space-separated integers Ni and Mi. 1≤T≤1021 0≤Mi<Ni≤109
输出¶
Output T lines each contains an integer representing the probability that first i scenarios will happen sequentially. you should output the number module 109+7(1000000007). Suppose the probability is P/Q, the desired output will be P×Q−1 mod 109+7
样例输入¶
样例输出¶
题解¶
打表找规律,打表代码如下:
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <ctime>
using namespace std;
int num[520];
bool vis[520];
int main(){
srand((unsigned)time(NULL));
int n;
while(cin >> n){
memset(num,0,sizeof(num));
for (int i = 1; i<= 10000000;i++){//数据开的大,跑得慢,耐心等待,可以调小一点
memset(vis,false,sizeof(vis));
int pos=0;
int cnt=1;
vis[0]=true;
while(cnt<n){
int x=rand()%2;
if(!x) x=-1;
pos+=x;
pos=(pos+n)%n;
if(!vis[pos]){
vis[pos]=true;
cnt++;
}
if(cnt==n){
num[pos]++;
}
}
}
for (int i = 0; i< n;i++){
cout << i << ": " << num[i] << endl;
}
}
return 0;
}
附:
-
C++产生任意区间的随机数:number = (rand()%(maxValue - minValue +1)) + minValue;
-
rand()会返回一随机数值, 范围在0至RAND_MAX 间。RAND_MAX定义在stdlib.h, 其值为2147483647。
-
srand()可用来设置rand()产生随机数时的随机数种子。通过设置不同的种子,我们可以获取不同的随机数序列。可以利用srand((int)(time(NULL))的方法,利用系统时钟,产生不同的随机数种子。
打表结论:如果n==1显然概率是1,如果m==0,从样例可以看出是0,其他情况是1/(n-1),
参考于:
https://blog.csdn.net/lgz0921/article/details/96695326
代码¶
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
ll ksm(ll a, ll b)
{
ll ans1 = 1, ans2 = a;
while (b != 0)
{
if (b % 2)
ans1 = (ans1 * ans2) % mod;
ans2 = (ans2 * ans2) % mod;
b /= 2;
}
return ans1;
}
int main()
{
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
ll t;
scanf("%lld", &t);
ll n, m, ans = 1;
while (t--)
{
scanf("%lld%lld", &n, &m);
if (n == 1)
ans *= 1;
else if (m == 0)
ans = 0;
else
ans = (ans*ksm(n - 1, mod - 2))%mod;
printf("%lld\n", ans);
}
}