您的位置:首页 > 其它

CF 456 div2 A,B,D,E 题解

2018-01-06 18:59 405 查看
传送门

A题: 水题直接做(注意爆int就是了)

B题: 注意看清楚题目是选不超过k个, 所以就很简单啦. 直接分情况讨论, k == 1时就输出n, k >= 2时就输出n的二进制位数全为1的那个数. (注意不要使用log2来计算, 会被卡精度! 比如二进制为58个1的那个数log2出来就等于58!)

AC Code

void solve()
{
ll n,k;
while(cin >> n >> k ) {
if (k == 1) {
cout << n << endl;
}
else {
ll ans = 1 ;
while(ans <= n) ans <<= 1;
cout << --ans << endl;
}
}
}


C懂题意也做不来.

D: 就是给你个n*m的格子, 可以在里面放养k条鱼, 每条鱼必须在不同的格子中, 随机撒网问扑到鱼的期望值是多少. 首先我们可以很简单的想到ans = 每次撒网区域中鱼的个数之和 / 总的撒网次数. 总的撒网次数好求就是(n-r+1)*(m-r+1). 那每次撒网区域中鱼的个数了, 如果暴力求就是n*m肯定不行. 所以我们转换思维求每条鱼可以被多少次撒网捉到, 也就是我们需要在n*m的格子中选取k个期望被捉到很多次的格子中, 然后向四周扩展要期望值最好的k个, 所以我们就可以从一定会被捉很多次的中间的那个位置其(n/2+1, m/2+1), 这个位置一定会被捉很多次, 然后向四周扩展, 用BFS, 然后就是对一个格子(x, y) 被网捉到的次数的计算 就是

times = (min(m+1, x+r) - max(x, r)) * (min(n+1, y+r) - max(y, r)); //可以画出来看看.

那么就可以写了, 注意用优先队列, 因为我们首先要期望值最高的出来.

AC Code

int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
ll n, m, r, k;
ll cal(ll x, ll y) {
return (min(m+1, x+r) - max(x, r)) * (min(n+1, y+r) - max(y, r));
// 横着 * 竖着
}

struct node {
int x, y;
bool operator < (const node & a) const {
return cal(x, y) < cal(a.x, a.y);
}
};
void solve() {
while(cin >> n >> m) {
cin >> r >> k ;
int sx = m/2 + 1; int sy = n/2 + 1 ;
priority_queue<node>q;
map<pii, bool>mp;
q.push(node{sx, sy});
mp[make_pair(sx, sy)] = 1;
db ans = 0;
for (int i = 1 ; i <= k ; i++) {
node k = q.top();
q.pop();

ans += 1.0*cal(k.x, k.y);
for (int j = 0 ; j < 4 ; j++) {
int xx = k.x + dx[j], yy = k.y + dy[j];
if (mp[make_pair(xx, yy)]) continue;
if (xx < 1 || xx > m || yy < 1 || yy > n) continue;
mp[make_pair(xx, yy)] = 1;
q.push(node{xx, yy});
}
}
ans /= 1.0*(n-r+1)*(m-r+1);
printf("%.10f\n",ans);
}
}


E: 题意就是给你n个不超过100的素数, 在不超过1e18的数中的素因子都在这n个中求第k大个数多少.

那么我们就要几个问题处理, 一是知道有哪些数了, 如何求第k大, 这个用二分很好求. 第二个就是如何求得这些数, 设D(N) 表示一个数集合, 其中所有的数的素数因子都在N中(因为D(N)中的数都可以用N中的数得到). 如果单独用一个数组存, D(N)个数可能多达8e8之多, 那么我们可以把这n个素数分成两堆A, B. 分别求出, 然后就可以用双指针的方法在O(D(A) + D(B)) 的时间中求出我们的数有多少小于等于它的. 具体实现请看代码. 还有就是如何分为两个集合了? 如果单纯前一半在A, 剩下的在B的话, D(A)也可能多达7e6,这也不行, 所以我们需要隔一个放在不同的A,B集合中, 这样D(A)最多1e6, 就符合我们的要求了. 如何求D(A)了, 我用的深搜.

具体请看代码

AC Code

void dfs(vector<ll>&a, vector<ll>&b, ll cur = 1, int pos = 0, ll bound = INF) {
if (pos >= a.size()) {           //DFS求D(N).
b.pb(cur);
return ;
}
while (bound) {
dfs(a, b, cur, pos + 1, bound);
cur *= a[pos];
bound /= a[pos];
}
}

vector<ll>x, y;
int cal(ll t) {
int j = 0;
ll ans = 0;
for (int i = 0 ; i < x.size() ; i++) {
while(j < y.size() && y[j] <= t / x[i]) j++;
ans += j;    //因为x是从大到小排序的.
}
return ans;
}

void solve()
{
int n;
while(cin >> n) {
vector<ll> a[2];
for (int i = 1 ; i <= n ; i++) {
ll u ; cin >> u;
a[i % 2 ].pb(u);
}
int k ; cin >> k ;
x.clear(); y.clear();
dfs(a[0], x);
dfs(a[1], y);
sort(x.rbegin(), x.rend());   //从大到小排序. 这样就可以使用双指针的方法求k大.
sort(y.begin(), y.end());
ll l = 1, r = INF, mid;
ll ans = 1;
while(r >= l) {
mid = (l+r) >> 1;
if (cal(mid) < k) {
l = mid + 1;
ans = l;
}
else r = mid - 1;
}
cout << ans << endl;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: