【补题】Codeforces AIM Tech Round 5 (ABCDEF)

版权声明:欢迎转载评论 https://blog.csdn.net/m0_37809890/article/details/82177139

1028A. Find Square

找B块的中心,记录左上角和右下角的坐标,取平均。

1028B. Unnatural Conditions

使a,b的各位和都大于等于n,a+b的各位和小于等于m。
构造a+b等于10的幂次,如a=4445,b=5555

1028C. Rectangles

给定网格上n个矩形,求至少被n-1个矩形都覆盖的一个点,保证有解。

两个矩形的交一定也是一个矩形(可能为空),所以可以将n个矩形的前缀和pre与后缀和suf都求出来。如果pre[i-1]与suf[i+1]相交不为空,就可以输出它们相交的任意一点。

struct Rec
{
    int b,l,u,r;
    Rec operator | (const Rec & x) 
    {
        return (Rec){max(b,x.b),max(l,x.l),min(u,x.u),min(r,x.r)};
    }
}save[M],pre[M],suf[M];
int main(void)
{
    int n = read();
    for(int i=1;i<=n;i++)
    {
        save[i].b = read();
        save[i].l = read();
        save[i].u = read();
        save[i].r = read();
    }
    pre[0] = suf[n+1] = (Rec){-(int)1e9,-(int)1e9,(int)1e9,(int)1e9};
    for(int i=1;i<=n;i++)
        pre[i] = pre[i-1] | save[i];
    for(int i=n;i;i--)
        suf[i] = suf[i+1] | save[i];
    for(int i=1;i<=n;i++)
    {
        Rec now = pre[i-1] | suf[i+1];
        if(now.b<=now.u && now.l<=now.r)
            return !printf("%d %d\n",now.b,now.l );
    }

    return 0;
}

学习了applese的代码

  1. 使用运算符重载来模拟矩形求交
  2. 使用(Rec){a,b,c,d}来新建矩形
  3. 使用return !printf()来快速结束程序,注意printf()的返回值为输出长度。
  4. 使用最大值(-1e9,-1e9,1e9,1e9)来充当边界值。

1028D. Order book

给定若干个ADD x和AC x操作,每个ADD的x可以为buy或sell,但是必须时刻保证当前最大的buy比最小的sell要小。AC只能AC最大的buy或最小的sell。所有x互不相同,给所有x分配buy或sell,问方法数有几种。

记录最大的buy与最小的sell。
如果当前AC的数字比最大buy小或者比最小sell大,那么矛盾。
如果当前AC的数字恰好等于它们中的一个,那么将其出队。
如果在它们之间,则方案数*2.
注意每次AC后,分配当前所有未分配的数字。
ADD操作,如果x比最大的buy小,则为buy,如果比最小的sell大,则为sell。
最后如果仍有n个未分配的数字,则方案数*(n+1)

实现:可以用三个set,或者两个pq,一个set。

set<int> all;
priority_queue<int> buy;
priority_queue<int,vector<int>,greater<int>> sell;
ll ans = 1;
void add(int t)
{
    if(sell.top()<t) //sell中有更小的
        sell.push(t);
    else if(buy.top()>t) //buy中有更大的
        buy.push(t);
    else
        all.insert(t);
}
void accept(int t)
{
    if(sell.top()<t || buy.top()>t)
        ans = 0;
    else if(sell.top()==t)
        sell.pop();
    else if(buy.top()==t)
        buy.pop();
    else //在all中
    {
        ans = (ans*2) % MOD;
        all.erase(t);
    }
    for(auto x:all)
    {
        if(x<t)
            buy.push(x);
        else                        
            sell.push(x);
    }
    all.clear();
}
int main(void)
{
    buy.push(0);
    sell.push(MOD);

    int n = read();
    char op[10];
    while(n--)
    {
        int t;
        scanf("%s %d",op,&t);
        op[1]=='D' ? add(t) : accept(t);
    }
    ans = ans * ((int)all.size()+1) %MOD;
    cout << ans << endl;
    return 0;
}
  1. 操作型的题都用函数,看起来简洁明了。
  2. 我一直WA4的原因是AC最小的sell或最大的buy后没有给其它未分配的值进行分配。
  3. 基本所有的stl判断空都可以用哨兵,简洁方便。

1028E. Restore Array

给定数组b,求数组a使得 a i mod a i + 1 = b i a n mod a 1 = b n

特判所有b相同的情况,若全为0则全部输出1,不为0则无解。
选择第一个数值最大的位置(前一个数不能与之相同),记为 s
a s = b s ,然后 a i = a i + 1 + b i i s 1 s + 1
这样做会过不了4 0 0 0这个数组样例,因为算一圈回来时数值还是4.
解决的办法是第一次碰到 b i = 0 a i = 2 a i + 1 + b i

ll save[M],ans[M];
int main(void)
{
    int n = read();
    ll mi= INT_MAX,mx = 0;
    for(int i =0;i<n;i++)
    {
        save[i] = read();
        mi = min(save[i],mi);
        mx = max(save[i],mx);
    }
    //判断全部相同的情况
    if(mi==mx)
    {
        if(mi)
            printf("NO\n");
        else
        {
            printf("YES\n");
            for(int i=0;i<n;i++)
                printf("%d ",1 );
            printf("\n");
        }
        return 0;
    }
    //寻找起始位置,要求最大且前面不能与之相同
    int st = 0;
    for(st=0;st<n;st++)
        if(save[st]==mx&&save[(st-1+n)%n]!=mx)
            break;
    //向后遍历
    int see0 = 0;
    ans[st] = save[st];
    for(int i=(st-1+n)%n;i!=st;i=(i-1+n)%n)
    {
        if(save[i]||see0)
            ans[i] = save[i] + ans[(i+1)%n];
        else
        {
            ans[i] = save[i] + ans[(i+1)%n]*2;
            see0 = 1;
        }

    }
    printf("YES\n");
    for(int i=0;i<n;i++)
        cout << ans[i] << " ";

    return 0;
}

F. Make Symmetrical

给定一个二维平面和q个操作,包括(1)插入一个点,保证这个位置没有点。(2)删除一个点,保证这个位置有点。(3)选择一个点,问最少需要插入多少个点可以使这个平面关于原点到此点的连线对称。

(看了题解又看了app的博客和代码才懂)
用数学方法可以证明距原点距离为定值d的格点不会很多(144个)。
记录每个距离上的点,每添加一个新点时遍历已有的点,把所有可能的对称轴的答案都更新。
删除同理,查询时直接输出答案即可。

struct Point
{
    int x,y;
    Point(int tx = 0, int ty = 0)
    {
        x = tx;
        y = ty;
    }
    Point simple() const
    {
        int t = __gcd(x,y);
        return Point(x/t,y/t);
    }
    Point operator + (const Point & b) const
    {
        return Point(x+b.x,y+b.y).simple();
    }
    bool operator < (const Point & b) const
    {
        return x==b.x?y<b.y:x<b.x;
    }
};
map<ll,set<Point>> exist; //某个距离上的点集合
map<Point,int> ans; //以原点到某点为对称轴时,已经存在的对称点数

int main(void)
{
    int q = read();
    int cnt = 0;
    while(q--)
    {
        ll op = read(), x = read(), y = read();
        ll d = x * x + y * y;
        Point now(x, y), spnow = now.simple();
        if(op == 1)
        {
            for(auto p : exist[d])
                ans[now + p] += 2;
            exist[d].insert(now);
            ans[spnow]++, cnt++;
        }
        else if(op == 2)
        {
            exist[d].erase(exist[d].find(now));
            for(auto p : exist[d])
                ans[now + p] -= 2;
            ans[spnow]--, cnt--;
        }
        else
            printf("%d\n", cnt - ans[spnow] );
    }

    return 0;
}

实现细节:

  1. 注意顺序,插入时要先遍历再插入到set中,删除时先删除再遍历。
  2. 每个对称轴都用距离原点最近的那个点来表示,最多可能有 2 Σ Φ ( i ) + 3 个,但这个非常大的数字并没有什么卵用,因为每次插入删除最多访问144个,每次查询最多访问1个
  3. 要在set或map的键中使用自定义数据结构,需要重载小于号或定义比较函数,且两个参数的const特性需要相同,即重载小于号时bool operator < (const Point & b) const,之后也需要写一个const。
  4. app也太强了吧。

猜你喜欢

转载自blog.csdn.net/m0_37809890/article/details/82177139