//https://img2018.cnblogs.com/blog/1646268/201908/1646268-20190806114008215-138720377.jpg

2023.10.6解题报告

2023.10.6

20 + 40 + 0 = 60

T1

一开始想到了类似二分图的转化,但是没想出来怎么构造答案。

我们考虑将区间按左端点为第一关键字,右端点为第二关键字排序,用一个小根堆来维护当前在堆内的最小的右端点。

考虑扫一遍排好序的区间,如果当前点的左端点大于等于堆顶的右端点,就一直弹,直到最后堆空或者与当前区间相交。

因为排序是优先按照左端点排序,所以堆内的区间,左端点一定小于当前枚举到的左端点,要是右端点大于当前区间的左端点,那就是相交了。

我们按照堆内元素的情况来分:

  • 当前堆内为空,那么当前的区间放哪边都可以,方案数乘 \(2\)

  • 当前堆内有一个,那么当前区间和堆内的元素绑定,方案数乘 \(1\)

  • 当前堆内有两个及以上,那么无解,输出 \(0\)

复杂度近似 \(O(n)\)

/*
 * @Author: Aisaka_Taiga
 * @Date: 2023-10-06 14:25:22
 * @LastEditTime: 2023-10-06 14:38:55
 * @LastEditors: Aisaka_Taiga
 * @FilePath: \Desktop\T1_100.cpp
 * The heart is higher than the sky, and life is thinner than paper.
 */
#include<bits/stdc++.h>

#define int long long
#define P 998244353
#define N 1000100

using namespace std;

inline int read()
{
    int x = 0, f = 1;
    char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') f = -1; c = getchar();}
    while(c <= '9' && c >= '0') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return x * f;
}

int n, ans;
struct node{int l, r;}e[N];
priority_queue<int, vector<int>, greater<int> > q;

inline int cmp(node a, node b){if(a.l == b.l) return a.r < b.r; return a.l < b.l;}

signed main()
{
    n = read();
    ans = 1;
    for(int i = 1; i <= n; i ++) e[i] = (node){read(), read()};
    sort(e + 1, e + n + 1, cmp);//按照左端点排序
    for(int i = 1; i <= n; i ++)
    {
        while(!q.empty() && q.top() <= e[i].l) q.pop();//把不相交的都去掉
        if(q.empty()) ans = ans * 2 % P;//空就代表没有与之相交的,放哪边都行
        if(q.size() > 1) return cout << '0', 0;//大于1个相交的,没有方案
        q.push(e[i].r);//将右端点放进去
    }
    cout << ans << endl;
    return 0;
}

赛时暴力代码:

/*
 * @Author: Aisaka_Taiga
 * @Date: 2023-10-06 09:14:35
 * @LastEditTime: 2023-10-06 09:22:03
 * @LastEditors: Aisaka_Taiga
 * @FilePath: \Desktop\T1.cpp
 * The heart is higher than the sky, and life is thinner than paper.
 */
#include <bits/stdc++.h>

#define int long long
#define N 1000100
#define endl '\n'

using namespace std;

inline int read()
{
    int x = 0, f = 1;
    char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') f = -1; c = getchar();}
    while(c <= '9' && c >= '0') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return x * f;
}

int n, ans, vis[N];
struct node{int l, r;}e[N];

inline int cmp(node a, node b)
{
    if(a.l == b.l) return a.r < b.r;
    return a.l < b.l;
}

inline void check()
{
    int l1 = -1, l2 = -1;
    for(int i = 1; i <= n; i ++)
    {
        if(vis[i] == 1){if(e[i].l < l1) return ; l1 = e[i].r;}
        if(vis[i] == 0){if(e[i].l < l2) return ; l2 = e[i].r;}
    }
    ans ++;
    return ;
}

inline void dfs(int x)
{
    if(x == n + 1) return check(), void();
    dfs(x + 1);
    vis[x] = 1;
    dfs(x + 1);
    vis[x] = 0;
    return ;
}

signed main()
{
    n = read();
    for(int i = 1; i <= n; i ++) e[i] = (node){read(), read()};
    sort(e + 1, e + n + 1, cmp);
    dfs(1);
    cout << ans << endl;
    return 0;
}

T2

首先手模一下,可以发现我们每次至少砍一次,那么最多只要 \(\log n\) 轮(上取整)。

然后考虑分几轮搞,既然不大那就直接枚举。

考虑假设一共分 \(k\) 次的话,那么设两次分别分 \(a - 1\)\(a + 1\) 次,那么其实跟分 \(a\) 次是等价的。

所以我们当前枚举到分 \(i\) 轮,那么我们平均每轮分 \(xx = n^{\frac{1}{i}}\) 次,但是这个是下取整,设 \(tm = xx^{i}\) 是我们当前分的次数最多能确定有多少人的情况,那么只要 \(tm\) 小于 \(n\),我们就给其中一轮多分一次。

不难想到在最劣的情况下每一个都多分一次 \(tm\) 就一定比 \(n\) 大了。

复杂度 \(O(T\log^{2}n)\).


/*
 * @Author: Aisaka_Taiga
 * @Date: 2023-10-06 16:08:40
 * @LastEditTime: 2023-10-06 16:22:55
 * @LastEditors: Aisaka_Taiga
 * @FilePath: \Desktop\T2_100.cpp
 * The heart is higher than the sky, and life is thinner than paper.
 */
#include<bits/stdc++.h>

#define int long long
#define N 1000100

using namespace std;

inline int read()
{
	int x = 0, f = 1;
	char c = getchar();
	while(c < '0' || c > '9'){if(c == '-') f = -1; c = getchar();}
	while(c <= '9' && c >= '0') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
	return x * f;
}

inline int ksm(int x, int y)
{
	int res = 1;
	while(y)
	{
		if(y & 1) res = res * x;
		x = x * x , y >>= 1;
	}
	return res;
}

signed main()
{
	int T = read();
	while(T--)
	{
		int n = read(), b = read(), a = read();
		int res = 1LL << 60;//最大值
		if(n == 1){cout << "0" << endl; continue;}//就1个那就不用找
		int lim = log2(n) + 1;//次数的上限,每轮至少砍一次
		for(int i = 1; i <= lim; i ++)//枚举轮数
		{//xx是当前人一轮有几个人(下取整),tm是每轮乘起来的最后的能检测的最大人数,K是砍的总次数,
			int xx = pow(n, 1.0 / i), tm = ksm(xx, i), K = (xx - 1) * i;
			while(tm < n) tm /= xx, tm *= (xx + 1), K ++;//加轮数,最坏都加1 tm一定就大于n了
			res = min(res, K * a + b * i);//取最优的答案,i是轮数,K是一共砍了几次
		}
		cout << res << endl;
	}
	return 0;
}

赛时暴力代码:

/*
 * @Author: Aisaka_Taiga
 * @Date: 2023-10-06 09:39:40
 * @LastEditTime: 2023-10-06 14:07:42
 * @LastEditors: Aisaka_Taiga
 * @FilePath: \Desktop\T2.cpp
 * The heart is higher than the sky, and life is thinner than paper.
 */
#include <bits/stdc++.h>

#define int long long
#define N 1000100
#define endl '\n'

using namespace std;

inline int read()
{
    int x = 0, f = 1;
    char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') f = -1; c = getchar();}
    while(c <= '9' && c >= '0') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return x * f;
}

int T, n, a, b, ans;

inline void dfs(int x, int sum)
{
    if(x == 1) return ans = min(sum, ans), void();
    int cc = sqrt(x);
    for(int i = 2; i <= x; i ++)
    {
        // if(x % i == 0)
        // {
            // int c1 = i, c2 = x / i;
            // dfs(c2, sum + a * c1 + b);
            // dfs(c1, sum + a * c2 + b);
        // }
        if(x % i == 0) dfs(x / i, sum + a * (i - 1) + b);
        else dfs(x / i + 1, sum + a * (i - 1) + b);
    }
    return ;
}

inline void work()
{
    n = read(), b = read(), a = read();
    ans = 1e18;
    dfs(n, 0);
    cout << ans << endl;
    return ;
}

signed main()
{
    T = read();
    while(T --) work();
    return 0;
}
/*
1
100000000 12 3
*/
posted @ 2023-10-06 21:05  北烛青澜  阅读(6)  评论(0编辑  收藏  举报