省选测试3

省选测试3

T1

​ 给出三维空间内的\(n\)条直线(每条直线用三维空间中的两个点表示), 要求选出一个直线的集合, 使得它们之间两两有交点, 问这个集合最大是多少.\(n <= 1000\)

​ 神仙题, 但是用乱搞过了23333

​ 先说一下乱搞的思路吧 : 首先可以\(O(n^2)\)的求出那些直线有交点, 然后有交点的连边.然后问题就转化成了求一个图的最大团.

​ 什么你说你不会求三维直线有没有交点? 我反手就是一个连接

​ 但是求最大团所需时间复杂度过高, 无法过\(n <= 1000\),的数据. 我们可以尝试每次将这个集合和中的直线看做一个排列, 每次随机打乱这个排列, 然后贪心的考虑, 若当前要加入的直线与最大团中的所有直线都有连边, 那么就加进去, 否则不加. 这样多随机几次就好了.


#include <bits/stdc++.h>

#define int long long

using namespace std;

inline long long read() {
    long long s = 0, f = 1; char ch;
    while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
    for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
    return s * f;
}

const int N = 1005;
const double eps = 1e-5;
int n, ans;
int p[N], tmp[N], Link[N][N];
struct point {
    int x, y, z;
    point() {}
    point(int X, int Y, int Z) { x = X; y = Y; z = Z; }
} ;
struct line { point p1, p2; } l[N];

point operator - (const point &a, const point &b) { return point(a.x - b.x, a.y - b.y, a.z - b.z); }

int Dot(point a, point b) { return a.x * b.x + a.y * b.y + a.z * b.z; }

point Cro(point a, point b) { return point(a.y * b.z - b.y * a.z, b.x * a.z - a.x * b.z, a.x * b.y - b.x * a.y); }

inline int dcmp(double x) {
    if(fabs(x) < eps) return 0;
    return x > 0 ? 1 : -1;
}

double Len(point a) {
    return sqrt(a.x * a.x + a.y * a.y + a.z * a.z);
}

signed main() {

    freopen("gentech.in","r",stdin); freopen("gentech.out","w",stdout);

    srand((unsigned)time(NULL)); clock_t bg = clock();
    n = read();
    for(int i = 1, x, y, z;i <= n; i++) {
        x = read(); y = read(); z = read();
        l[i].p1 = point(x, y, z);
        x = read(); y = read(); z = read();
        l[i].p2 = point(x, y, z);
    }
    for(int i = 1;i <= n; i++) {
        for(int j = i + 1;j <= n; j++) {
            int tmp = Len(Cro(l[i].p2 - l[i].p1, l[j].p2 - l[j].p1));
            if(!dcmp(tmp)) continue ;
            point fv = Cro(l[i].p1 - l[i].p2, l[i].p2 - l[j].p1);
            tmp = Dot(fv, l[j].p2 - l[j].p1);
            if(!dcmp(tmp)) Link[i][j] = Link[j][i] = 1; 
        }
    }
    // for(int i = 1;i <= n; i++) 
    //     for(int j = 1;j <= n; j++) cout << i << " " << j << " " << Link[i][j] << "\n";
    for(int i = 1;i <= n; i++) p[i] = i;
    while((int) (clock() - bg) <= 1.3 * CLOCKS_PER_SEC) {
        for(register int i = 1;i <= n; i++) swap(p[rand() % n + 1], p[rand() % n + 1]);
        // for(int j = 1;j <= n; j++) cout << p[j] << " "; cout << "\n";
        int cnt = 0;
        for(register int i = 1;i <= n; i++) {
            if(!cnt) tmp[++ cnt] = p[i];
            else {
                int f = 1;
                for(register int j = 1;j <= cnt; j++) 
                    if(!Link[tmp[j]][p[i]]) { f = 0; break; }
                if(f) tmp[++ cnt] = p[i];
            }
        }
        ans = max(ans, cnt);
    }
    printf("%lld", ans);
    

    fclose(stdin); fclose(stdout);

    return 0;
}

/*
4
0 0 0 1 0 1 
0 0 1 1 1 1 
0 1 1 1 1 0 
0 1 0 1 0 0

3
-1 -1 0 3 3 0 
0 2 -1 -1 3 -2 
-2 0 -1 6 0 3
*/

T2

​ 有一个序列,初始时为空。你会不停地往序列末尾添加一个\([1, m]\)中的随机整数。

​ 任意时刻:

​ 若序列末尾的两个整数相同(记为 \(𝑥\))且小于 \(𝑡\),则这两个数会合并为 \(x + 1\)

​ 若序列长度为 𝑛 且无法合并,则操作结束。

​ 求操作结束时,序列中所有数的和的期望,答案对 \(10^9 + 7\) 取模。

\(1 <= n, m <= 10^3, m <= t <= 10^9\)

​ 更TM神仙, 还不会.

T3

​ 敌方有\(n\)台机器人, 每台机器人都有一个攻击值\(A\), 护甲值\(D\). 我方有一台机器人, 攻击值为\(ATK\).

​ 战斗为回合制, 规则如下:

​ 1.我方机器人选择敌方一台机器人进行攻击, 每次令其护甲值减少\(ATK\), 当护甲值小于等于0时, 这个机器人则被破坏.

​ 2.敌方每台未被破坏的机器人攻击我方机器人造成\(A_i\)的损失.

​ 但是在第一回合开始之前, 已经有两台敌方机器人被破坏了. 问最好情况下, 我方机器人收到多少损失.

\(n <= 3e5, 1 <= A, D, ATK <= 10^4\).

​ 贪心 + 平衡树.

​ 首先我们可以知道的是, 攻击敌方机器人一定是先把一个打死, 再去打下一个, 不可能把第一个打到半血, 然后去打另一个, 这样显然不优.

​ 假设我们没有一开始删除两个机器人, 那我们应该如何确定攻击顺序的. 我们考虑贪心的微扰法(就是国王游戏那种方法) :

​ 令\(P_i = \lceil \frac{D_i}{ATK}\rceil\).

​ 假设我们当前要确定先打机器人\(i\)再打机器人\(j\)的方式要比先打\(j\)后打\(i\)的方式要收到的损失小, 那么我们可以得到 :

\((P_i - 1) * A_i + (P_i+P_j - 1) * A_j < (P_j - 1)*A_j + (P_j +P_i - 1)*A_i\)

​ 化简一下 :

\(P_i*A_j < P_j*A_i\).

​ 所以我们按照这个对原序列排序, 即可得到最优的攻击顺序.

​ 但是呢? 我们一开始要删除两个机器人, 这就有点不好办了, 我们先考虑删除一个机器人的影响是什么 :

\(S_i = (P_i - 1)*A_i + A_i *\sum_{j=1}^{i-1}P_j + P_i * \sum_{j = i + 1}^n A_j\).

​ 那我们删除两个机器人的代价就应该是 : \(S_i +S_j - P_i * A_j(i< j)\).

​ 枚举两个时间复杂度不行, 那就枚举一个\(i\), 发现上面的式子很像斜率优化的式子 : \(f(i) = S_i + S_j - P_i * A_j\), 移项后得到 : \(S_j = P_i * A_j +f(i) - S_i\).

​ 我们把大于\(i\)的那些\(j\)都看成点\((A_j, S_j)\), 把\(P_i\)看做是斜率, 那我们要找的就是过某一个点得到最大的截距.

​ 所以我们可以从大到小枚举\(i\), 然后用平衡树维护一个上凸壳, 在上凸壳上二分那个截距最大的点就好了.

\(O(nlogn)\)

​ 当然可以用\(set\), 但是炒鸡麻烦, 还不如直接写平衡树.

#include <bits/stdc++.h>

using namespace std;

inline long long read() {
    long long s = 0, f = 1; char ch;
    while(!isdigit(ch = getchar())) (ch == '-') && (f = -f);
    for(s = ch ^ 48;isdigit(ch = getchar()); s = (s << 1) + (s << 3) + (ch ^ 48));
    return s * f;
}

const int N = 3e5 + 5;
int n, tot, ATK;
long long sumP[N], sumA[N], S[N];
struct node { int A, D, P; } a[N];

int cmp(node i, node j) {
    return i.P * j.A < j.P * i.A;
}

const double eps = 1e-14;
struct point { 
    double x, y; int id;
    point() {}
    point(long long X, long long Y, int Z) { x = X; y = Y; id = Z; }
    friend int operator < (const point &a, const point &b) {
        return a.x == b.x ? a.y < b.y : a.x < b.x;
    }
} ;
set <point> s;
set <pair<double, int> > sd;

typedef point Vector ;
point operator - (const point &a, const point &b) { return point(a.x - b.x, a.y - b.y, a.id); }
double Cro(Vector a, Vector b) { return a.x * b.y - a.y * b.x; }

inline int dcmp(double x) {
    if(fabs(x) < eps) return 0;
    return x > 0 ? 1 : -1;
}

inline double calc_xl(point a, point b) {
    if(fabs(a.x - b.x) < eps) {
        if(fabs(a.y - b.y) < eps) return 0;
        else if(b.y > a.y) return 1e18;
        else return -1e18;
    }
    else return (b.y - a.y) / (b.x - a.x);
}

void Insert(point x) {
    set <point> :: iterator itl, itr, it;
    set <pair<double, int> > :: iterator is;
    itr = s.lower_bound(x);
    itl = itr; 
    if(itl == s.begin()) {
        while(itr != s.end()) {
            it = itr; itr ++;
            if(itr == s.end() || dcmp(Cro(x - *itr, *it - *itr)) < 0) break ;
            is = sd.find(make_pair(-calc_xl(*it, *itr), (*it).id));
            s.erase(it); sd.erase(is); tot --;
        }
    }
    else {
        itl --;
        if(dcmp(Cro(x - *itr, *itl - *itr)) < 0) return ;
        while(itr != s.end()) {
            it = itr; itr ++;
            if(itr == s.end() || dcmp(Cro(x - *itr, *it - *itr)) < 0) break ;
            is = sd.find(make_pair(-calc_xl(*it, *itr), (*it).id));
            s.erase(it); sd.erase(is); tot --;
        }
        while(itl != s.begin()) {
            it = itl; itl --;
            if(dcmp(Cro(x - *itl, *it - *itl)) > 0) break ;
            is = sd.find(make_pair(-calc_xl(*itl, *it), (*itl).id));
            s.erase(it); sd.erase(is);
        }
    }
    s.insert(x);  
    tot ++;
    it = s.find(x);
    if(it != s.begin()) {
        itl = it; itl --;
        sd.insert(make_pair(-calc_xl(*itl, *it), (*itl).id));
    }
    if(it != s.end()) {
        itr = it; itr ++;
        if(itr != s.end()) {
            sd.insert(make_pair(-calc_xl(*it, *itr), (*it).id));
        }
    }
}

long long calc(int d, int i) {
    set <point> :: iterator itd;
    itd = s.end(); itd --;
    long long res = S[i] + S[(*itd).id] - a[i].P * a[(*itd).id].A;
    if(sd.size()) {
        set <pair<double, int> > :: iterator it;
        it = sd.lower_bound(make_pair(d, i));
        if((*it).second != 0) {
            res = max(res, S[i] + S[(*it).second] - a[i].P * a[(*it).second].A);
        }
        else {
            it = sd.end(); it --;
            res = max(res, S[i] + S[(*it).second] - a[i].P * a[(*it).second].A);
        }
    }
    return res;
}

int main() {

    freopen("fittest.in","r",stdin); freopen("fittest.out","w",stdout);

    n = read(); ATK = read();
    for(int i = 1;i <= n; i++) {
        a[i].A = read(); a[i].D = read();
        a[i].P = ceil(1.0 * a[i].D / ATK);
    }
    sort(a + 1, a + n + 1, cmp);
    for(int i = 1;i <= n; i++) sumP[i] = sumP[i - 1] + a[i].P;
    for(int i = n;i >= 1; i--) sumA[i] = sumA[i + 1] + a[i].A;
    for(int i = 1;i <= n; i++) 
        S[i] = (a[i].P - 1) * a[i].A + sumP[i - 1] * a[i].A + sumA[i + 1] * a[i].P;
    s.insert(point(a[n].A, S[n], n));  tot ++;
    long long ans = 1e18, res = 0, H = 0;
    for(int i = 1;i <= n; i++) {
        H += a[i].P; res += 1ll * (H - 1) * a[i].A;
    }
    for(int i = n - 1;i >= 1; i--) {
        ans = min(ans, res - calc(-a[i].P, i));
        Insert(point(a[i].A, S[i], i));
    }
    printf("%lld\n", ans);

    fclose(stdin); fclose(stdout);

    return 0;
}

/*
5 10
21 9
83 9
2 9
11 1
123 1

3 7 
30 8 
7 35 
1 209
*/
posted @ 2021-03-03 07:35  C锥  阅读(66)  评论(0编辑  收藏  举报