2021-icpc网络赛第二场-M.J.G.L(后续补题中)

M.Addition

别算出C的十进制再来想怎么转化为二进制,因为,如果C<0,就不好做了。
直接一位一位的相加,再进行分类讨论就好了。
如果这题n的范围是1e5这种大数据,比赛的时候或许就会想直接按位相加,而不是算出十进制再转化了吧。。。偏偏32<=n<=60,不禁让人想要算出C是多少。

思路:
如果当前a [ i ] + b [ i ] = 1,则c [ i ] = 1;
如果a [ i ] + b [ i ] = 2,就要开始进位了,再来一层循环,从int j = i + 1开始循环,
如果 sign [ j ] = sign [ i ],那么就像普通的二进制加法一样,该进位就进位,不该进位就不用进位,这里不再细谈。
• 如果 sign[ j ] != sign [ i ]了,因为符号不同了,相当于出现了减法,不再是单纯的二进制加法,所以就要像十进制减法一样,我们考虑“借位”。
• 若a [ j ] + b [ j ] = 2,本来此位要往后进一位,但是此位的前一位符号相反,且往此位进了以为,所以抵消掉了一半,故c [ j ] = 1,且break,因为不需要往后进位了。
• 若a [ j ] + b [ j ] = 1,类似于上面的抵消,c [ j ] = 0,且break。
• 若a [ j ] + b [ j ] = 0,这种情况,就该“借位”了,向此位的后一位借了1,来进行类似于上面的抵消,借了得还,也就相当于后面的位要进行进位操作,所以不退出这个进位循环。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 64 + 5, mod = 998244353;
int a[N], b[N], p[N], c[N];
signed main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> p[i];
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) cin >> b[i];
    for (int i = 1; i <= n; i++) {
        int x = a[i] + b[i];
        if (x == 1) c[i] = 1;
        else if (x == 2) {
            int j;
            for (j = i + 1; j <= n; j++) {
                int y = a[j] + b[j];
                if (p[j] == p[i]) {
                    if (y == 2) c[j] = 1;
                    else if (y == 1) ;
                    else if (y == 0) {c[j] = 1; break;}
                }
                else {
                    if (y == 2) {c[j] = 1; break;}
                    else if (y == 1) {break;}
                    else if (y == 0) c[j] = 1;
                }
            }
            i = j;
        }
    }
    for (int i = 1 ; i <= n; i++) {
        cout << c[i];
        if (i != n) cout << " ";
    }
    return 0;
}

J.Leaking Roof

就是个模拟题,细节有点多,题千万别读错,否则极其浪费时间。
格子的水会向和它相邻的高度比他低的格子流动,问所有高度0的格子最后有多少水。
思路:优先队列,按高度从高到低排序,按此顺序直接模拟即可。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 500 + 5, INF = 0x3f3f3f3f;
struct node{
    double val;
    int x, y;
    int h;
    bool operator < (const node &k) const {
        if (h != k.h) return h < k.h;
        else if (x != k.x) return x < k.x;
        return y < k.y;
    }
}a[N][N];
priority_queue<node> q;
int n;
node b[5];
int cnt;
bool book[N][N];
void ck(int i, int j)
{
    if (i >= 1 && i <= n && j >= 1 && j <= n && book[i][j] == 0) {
        b[++cnt] = a[i][j];
    }
}
signed main()
{
    double m;
    cin >> n >> m;
    memset(a, INF, sizeof(a));
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> a[i][j].h;
            a[i][j].x = i, a[i][j].y = j;
            a[i][j].val = m;
            if (a[i][j].h != 0) q.push(a[i][j]);
        }
    }
    while(!q.empty()) {
        int x = q.top().x, y = q.top().y;
        q.pop();
        book[x][y] = 1;
        cnt = 0;
        ck(x - 1, y), ck(x + 1, y), ck(x, y - 1), ck(x, y + 1);
        sort(b + 1, b + 1 + cnt);
        int num = 0;
        for (int i = 1; i <= cnt; i++) {
            if (b[i].h < a[x][y].h) num++;
            else break;
        }
        for (int i = 1; i <= num; i++) a[b[i].x][b[i].y].val += a[x][y].val / num; 
        a[x][y].val = 0;
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (a[i][j].val != 0) printf("%.6f ", a[i][j].val);
            else printf("0 ");
        }
        printf("\n");
    }
    return 0;
}

G.Limit

分子可以泰勒展开成x的幂次,也就可以和分母约分了,然后判断极限的情况。
在这里插入图片描述

第25行的循环,意思是小于x的t次方的系数如果不为零,那么极限就是无穷。
最后再和t进行约分,中间量一直用int不用double可以避免精度损失。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5, INF = 0x3f3f3f3f;
int a[N], b[N], f[N];
int t;
void taile(int x, int y)
{
    for (int i = 1; i <= t; i++) {
        f[i] += x * pow(-1, i + 1) * pow(y, i);
    }
}
signed main()
{
    int n;
    cin >> n >> t;
    for (int i = 1; i <= n; i++) {
        scanf("%lld%lld", &a[i], &b[i]);
        taile(a[i], b[i]);
    }
    if (t == 0) {
        cout << 0;
        return 0;
    }
    for (int i = 1; i < t; i++) {
        if (f[i] != 0) {
            cout << "infinity";
            return 0;
        }
    }
    int fz = f[t], fm = t;
    int gcd = __gcd(fz, fm);
    fz /= gcd, fm /= gcd;
    if (fm == 1) printf("%lld", fz);
    else printf("%lld/%lld", fz, fm);
    return 0;
}

L.Euler Function

看巨巨的博客才会写的,一开始知道是用势能线段树,但是不知道该怎么去维护一个区间该不该暴力,我想着既然是想知道区间的数是不是质数p的倍数,那就维护个区间gcd吧。
然后就TLE了无数次。。
因为100以内有25个质数,他们的公共gcd就是他们的积,然而这个积是爆long long的,所以log(N)的修改也就随着gcd的爆炸变成了O(N)的修改,所以tle了。
正解是用线段树同时维护一个bitset数组(bool数组也行),这个bit数组的第 i 位的意义是,区间所有的数是否为第 i 个质数的倍数。
期间也wa了很多回,有很多细节没有注意到:
1.运算符&是比=号低级的,所以要打上一个括号。
2.pushdown函数,左右孩子的权值,懒标记,均是乘以父亲的懒标记。
3.query函数,求和的时候,不要忘记取模。

巨巨的博客
我的代码和他的大同小异了。

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5, mod = 998244353;
int a[N], oula[N];
bitset<30> st[105];
struct node{
    int l, r, val;
    bitset<30> bt;
    int add = 1;
    #define l(x) tree[x].l
    #define r(x) tree[x].r
} tree[N * 4];
int cnt;
int prime[N];
bool v[N];
int biao[200][200];
int val;
vector<int> g[105];
int phi(int n)
{
    int ret = n;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            ret = ret / i * (i - 1);
            while(n % i == 0) n /= i;
        }
    }
    if (n > 1) ret = ret / n * (n - 1);
    return ret;
}
void init()
{
    for (int i = 2; i <= 100; i++) {
        if (!v[i]) prime[++cnt] = i;
        for (int j = 1; j <= cnt; j++) {
            if (i * prime[j] > 100) break;
            v[i * prime[j]] = 1;
            if (i % prime[j] == 0) break;
        }
    }
    for (int i = 1; i <= 100; i++) oula[i] = phi(i);
    for (int i = 1; i <= 100; i++) {
        int num = i;
        for (int j = 1; j <= cnt; j++) {
            if (num % prime[j] == 0) {
                g[i].push_back(j);
                st[i][j] = 1;
                while(num % prime[j] == 0){
                    num /= prime[j];
                    biao[i][j]++;
                }
            }
        }
    }
}
void build(int now, int l, int r)
{
    l(now) = l, r(now) = r;
    if (l == r) {
        tree[now].val = oula[a[l]];
        // printf("oula(%lld) = %lld\n", a[l], oula(a[l]));
        tree[now].bt = st[a[l]];
        return;
    }
    int mid = (l + r) >> 1;
    int chl = 2 * now, chr = 2 * now + 1;
    build(chl, l, mid);
    build(chr, mid + 1, r);
    tree[now].val = (tree[chl].val + tree[chr].val) % mod;
    tree[now].bt = (tree[chl].bt & tree[chr].bt);
}
void down(int now)
{
    if (tree[now].add != 1) {
        int chl = 2 * now, chr = 2 * now + 1;
        tree[chl].add = tree[chl].add * tree[now].add % mod;
        tree[chr].add = tree[chr].add * tree[now].add % mod;
        tree[chl].val = tree[chl].val * tree[now].add % mod;
        tree[chr].val = tree[chr].val * tree[now].add % mod;
        tree[now].add = 1;
    }
}
void up(int now, int l, int r, int k)
{
    if (l <= l(now) && r >= r(now) && tree[now].bt[k]) {
        for (int i = 1; i <= biao[val][k]; i++) {
            tree[now].val = tree[now].val * prime[k] % mod;
            tree[now].add = tree[now].add * prime[k] % mod;
        }
        return;
    }
    if (l(now) == r(now)) {
        tree[now].val = tree[now].val * (prime[k] - 1) % mod;
        for (int i = 1; i <= biao[val][k] - 1; i++) {
            tree[now].val = tree[now].val * prime[k] % mod;
        }
        tree[now].bt[k] = 1;
        return;
    } 
    down(now);
    int mid = (l(now) + r(now)) >> 1;
    int chl = 2 * now, chr = 2 * now + 1;
    if (l <= mid) up(chl, l, r, k);
    if (r >  mid) up(chr, l, r, k);
    tree[now].val = (tree[chl].val + tree[chr].val) % mod;
    tree[now].bt = (tree[chl].bt & tree[chr].bt);
}
int que(int now, int l, int r)
{
    if (l <= l(now) && r >= r(now)) {
        return tree[now].val;
    }
    down(now);
    int mid = (l(now) + r(now)) >> 1;
    int chl = 2 * now, chr = 2 * now + 1;
    int ret = 0;
    if (l <= mid) ret = (ret + que(chl, l, r)) % mod;
    if (r >  mid) ret = (ret + que(chr, l, r)) % mod;
    return ret;
}
signed main()
{
    init();
    int n, m;
    scanf("%lld%lld", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    build(1, 1, n);
    while(m--) {
        int f, l, r;
        scanf("%lld%lld%lld", &f, &l, &r);
        if (f == 0) {
            scanf("%lld", &val);
            int len = g[val].size();
            for (int i = 0; i < len; i++) up(1, l, r, g[val][i]);
        }
        else {
            printf("%lld\n", que(1, l, r));
        }
    }
    return 0;
}

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值