CSP-S模拟18

A. 最长反链

我居然以为它不连续的也不行,我怀疑这个20分的东西把题面改成“x不是y的子序列”大概是对的。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1337;

int T, l, r, h1, t1, Max, h2;

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

int qpow(int a, int b)
{
    int ans = 1;
    while(b)
    {
        if(b & 1) ans = ans * a;
        a = a * a;
        b >>= 1;
    }
    return ans;
}

int main()
{
    T = read();
    while(T--)
    {
        l = read(); r = read();
        int ll = l, rr = r;
        h1 = 0, t1 = 0;
        while(rr)
        {
            h1++; t1 = rr % 10;
            rr /= 10;
        }
        Max = qpow(10, h1-1);
        h2 = 0;
        while(ll)
        {
            h2++; ll /= 10;
        }
        if(h1 == h2)
        {
            printf("%d\n", r-l+1); continue;
        }
        if(t1 != 1)
        {
            printf("%d\n", r-Max+1); continue;
        }
        int base = r % Max;
        int m2 = qpow(10, h1-2);
        if(base < m2) base += m2;
        if(base < l) base = l - 1;
        printf("%d\n", r-base);
    }

    return 0;
}
不审题

然而我赛时连这20都没有,因为我算出来的l没有和题目里给的l取min。。。

于是改成连续的就可以了:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1337;

int T, l, r, h1, t1, Max, h2;

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

int qpow(int a, int b)
{
    int ans = 1;
    while(b)
    {
        if(b & 1) ans = ans * a;
        a = a * a;
        b >>= 1;
    }
    return ans;
}

int main()
{
    T = read();
    while(T--)
    {
        l = read(); r = read();
        int ll = l, rr = r;
        h1 = 0, t1 = 0;
        while(rr)
        {
            h1++; t1 = rr % 10;
            rr /= 10;
        }
        Max = qpow(10, h1-1);
        h2 = 0;
        while(ll)
        {
            h2++; ll /= 10;
        }
        if(h1 == h2)
        {
            printf("%d\n", r-l+1); continue;
        }
        if(t1 != 1)
        {
            printf("%d\n", r-Max+1); continue;
        }
        int base = r % Max;
        int m2 = r / 10;
        if(base < m2) base = m2;
        if(base < l) base = l - 1;
        printf("%d\n", r-base);
    }

    return 0;
}
View Code

 

B. 2A+x

WA 51:先分两种大的情况,一种是所有点都需要操作,一种是只有一部分点需要操作。所有点操作一遍的最好情况就是差值变成max(2*原始差值-x, 0),也就是2*原始差值-x<原始差值,原始差值<x。这种情况无限操作就可以无限减小差值,差值总会变成0。

然后对于剩下的,a最大的那个一定不会被操作,我保存了每个数尽量靠近它的两个值(小于等于的最大mi和大于等于的最小mx),操作的结果被我分成3种情况,原来的最大a记为Max,全取mi,Max依然最大,全取mx,Max成为最小,还有都尽量取绝对值小的,Max什么都不是。

错因可能是第3种情况我对于操作后不满足Max什么都不是的直接弃掉了,但是其实可能会出更优答案。是不是这么错的其实我也不很确定……

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e5 + 6;
const ll inf = 1e13;

int n, v, a[maxn];
ll d[maxn], mi[maxn], mx[maxn], Max, ans;

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

ll qpow(ll a, ll b)
{
    ll ans = 1;
    while(b)
    {
        if(b & 1) ans = ans * a;
        a = a * a;
        b >>= 1;
    }
    return ans;
}

int main()
{
    n = read(); v = read();
    for(int i=1; i<=n; i++) a[i] = read();
    sort(a+1, a+1+n);
    if(a[n] - a[1] <= v)
    {
        printf("0\n"); exit(0);
    }
    Max = a[n];
    for(int i=1; i<n; i++)
    {
        mi[i] = a[i]; mx[i] = inf;
        for(int j=1; true; j++)
        {
            ll f = qpow(2, j);
            if(a[i]*f+v*(f-1) >= Max)
            {
                mx[i] = min(mx[i], max(a[i]*f,Max));
            }
            if(a[i]*f <= Max)
            {
                mi[i] = max(mi[i], min(a[i]*f+v*(f-1),Max));
            }
            else break;
        }
    }
    memcpy(d, mx, sizeof(mx));
    sort(d+1, d+n);
    ans = d[n-1] - Max;
    memcpy(d, mi, sizeof(mi));
    sort(d+1, d+n);
    ans = min(ans, Max - d[1]);
    for(int i=1; i<n; i++)
    {
        if(Max - mi[i] <= mx[i] - Max) d[i] = mi[i];
        else d[i] = mx[i];
    }
    sort(d+1, d+n);
    if(d[1] < Max && d[n-1] > Max) ans = min(ans, d[n-1] - d[1]);
    printf("%lld\n", ans);

    return 0;
}
WA 51

然后我鹤了sandom的题解:首先我发现了找到步数不需要循环,可以用log2函数,还有可以先缩小一下差距再和x比较,全取mi让Max依然最大是一样的,接下来把修改后的数组仍然按a排序讨论取mx的情况。因为a已经排过序了,如果选到了一个值让它mi->mx,那它左边的都可以这样做,因为mi->mx其实就是左端点*2,比它更小的*2之后一定依然小于它,当前的最小值就是没被操作的那个数的mi。

这个过程没有更改a的值,所以我把最后一个排序删掉了。并且根据最后一个数不需要被操作的原理,我把所有的n从循环里去掉了,但是n的b值(“修改”后的mi也就是小于等于的最大值)需要赋一下初值就是它自己,我的魔改应该是对的实测可以过。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e5 + 6;
const ll inf = 1e13;

int n;
ll x, ans = 1e18, bow[60];

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

struct node 
{
    ll a, k, b;
    bool operator < (const node T) const 
    {
        return a < T.a;
    }
}d[maxn], e[maxn];

int main()
{
    bow[0] = 1;
    for(int i=1; i<=50; i++) bow[i] = bow[i-1] * 2;
    n = read(); x = read();
    for(int i=1; i<=n; i++) d[i].a = read();
    sort(d+1, d+1+n);
    for(int i=1; i<n; i++)
    {
        //1.这个区间可以一步确定!!2^k可以预处理
        d[i].k = log2(d[n].a/d[i].a);//下限可以直接算,k是步数
        d[i].a = bow[d[i].k]*d[i].a;//直接扩大
        if(d[i].a+(bow[d[i].k]-1)*x>=d[n].a) d[i].a = d[n].a, d[i].k = 0;//如果可以相等
        d[i].b = d[i].a + (bow[d[i].k]-1)*x;//否则取的是小于等于的最大值
        //不需要取大于等于的最小值吗??
        //不需要分类讨论吗??
    }
    d[n].b = d[n].a;
    sort(d+1, d+1+n);
    ll gap = 1e18, pos = 0;
    for(int i=1; i<n; i++) gap = min(gap, d[i].b);
    //2.全体乘2不一定在一开始,还可以是先缩小差距
    //这个我大概是错了
    if(d[n].a - gap < x) 
    {
        printf("0\n"); exit(0);
    }
    ans = min(ans, d[n].a-gap);
    //都不乘2还是不都乘2?
    gap = d[n].b;//让最后一个数当一次最小值
    for(int i=n-1; i>=1; i--)
    {
        ans = min(ans, 2*d[i].a-gap);//分类讨论在这里!!让每个数尝试取一下较大值
        //然而最大和最小怎么可能同时让一个数取?这里指n
        gap = min(gap, d[i].b);
    }
    //sort(d+1, d+1+n);
    //printf("%lld\n", min(ans, d[n].a-d[1].a));
    printf("%lld\n", ans);

    return 0;
}
View Code

 

C. No Rest for the Wicked

我写了一个暴力是按美丽度从大到小从终点到起点的那种,更新方式是bfs,然而0分了(几乎全T还有错的),目前还很迷惑……

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 2e5 + 6;
const int inf = 1e9 + 7;

int n, m, c[maxn], t[maxn], s[maxn], val[maxn], cnt, mi[maxn];
bool vis[maxn]; 
queue<pair<int, int> > q;

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

struct node1
{
    int id, c, t, s;
    bool operator < (const node1 &T) const 
    {
        return s > T.s;
    }
}p[maxn];

struct node 
{
    int next, to;
}a[maxn<<1];
int head[maxn], len;

void add(int x, int y)
{
    a[++len].to = y; a[len].next = head[x];
    head[x] = len;
}

int bfs(int st)
{
    int ans = 1;
    val[p[st].id] = p[st].s;
    q.push(make_pair(p[st].t, p[st].id));
    while(!q.empty())
    {
        int lim = q.front().first, x = q.front().second;
        q.pop(); vis[x] = 1;
        if(val[x] < p[st].s) 
        {
            val[x] = p[st].s; ans++;
        }
        for(int i=head[x]; i; i=a[i].next)
        {
            int v = a[i].to;
            if(c[v] <= lim && (!vis[v] || min(lim, t[v]) > mi[i]))
            {
                mi[i] = min(lim, t[v]);
                q.push(make_pair(mi[i], v));
            }
        }
    }
    return ans;
}

int main()
{
    n = read(); m = read();
    for(int i=1; i<=n; i++)
    {
        p[i].id = i;
        p[i].c = read(); p[i].t = read(); p[i].s = read();
        c[i] = p[i].c; t[i] = p[i].t; s[i] = p[i].s;
    }
    sort(p+1, p+1+n); cnt = n;
    for(int i=1; i<=m; i++)
    {
        int x = read(), y = read();
        if(c[x] <= t[y]) add(x, y); 
        if(c[y] <= t[x]) add(y, x);
    }
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++) vis[j] = 0;
        for(int j=1; j<=n; j++) mi[i] = t[i];
        int del = bfs(i);
        cnt -= del;
        if(!cnt) break;
    }
    for(int i=1; i<=n; i++) printf("%d ", val[i]);

    return 0;
}
00000

于是我就去鹤了一下有分版的暴力,发现我的写法几乎没有什么思考,没有记录答案因为没有想到“考虑f(x, v)表示从疫情值为x的点v出发能够得到的最大美丽度,那么后面再扫到v并且新的疫情值小于等于x时就没有再搜下去的必要了,直接取f(x, v)即可”,我让它一直进去搜,搜了一遍再搜一遍,不T才怪……

//鹤:
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 2e5 + 6;
const int inf = 1e9 + 7;

int n, m, ans[maxn], st, minnt[maxn];

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

struct node 
{
    int next, to;
}e[maxn<<1];
int head[maxn], len;

void add(int x, int y)
{
    e[++len].to = y; e[len].next = head[x];
    head[x] = len;
}

struct city 
{
    int s, t, c;
}a[maxn];

struct my 
{
    int s, id;
    bool operator < (const my &T) const 
    {
        return s > T.s;
    }
}b[maxn];

void dfs(int x, int mint)
{
    ans[x] = max(a[st].s, ans[x]);
    minnt[x] = max(minnt[x], mint);
    for(int i=head[x]; i; i=e[i].next)
    {
        int v = e[i].to;
        //要么可以更新答案,要么可以放宽上界
        if((ans[v]<a[st].s||minnt[v]<mint) && a[v].c<=mint)
        {
            dfs(v, min(mint, a[v].t));
        }
    }
}

int main()
{
    n = read(); m = read();
    for(int i=1; i<=n; i++)
    {
        a[i].c = read(); a[i].t = read(); a[i].s = read();
        b[i].s = a[i].s; b[i].id = i;
    }
    sort(b+1, b+1+n);
    for(int i=1; i<=m; i++)
    {
        int x = read(), y = read();
        add(x, y); add(y, x);
    }
    for(int i=1; i<=n; i++)
    {
        st = b[i].id; dfs(st, a[st].t);
    }
    for(int i=1; i<=n; i++)
    {
        printf("%d ", ans[i]);
    }

    return 0;
}
View Code

正解是线段树分治(动态图连通性),我又想直接粘图了,from Chen_jr:

关于单向边的那个式子,我觉得意思是不一定能从j走回i,好像只判断了单向走可行但是并没有说反向不行,所以双向边也包含了单向边,不过建重复了无所谓反正时间是一样的,同时加入同时删除对连通性不造成影响。

一般的线段树分治节点都代表时间,因为一个边是否存在都和时间有关,但是在这道题里边的存在和疫情的标准有关(下界?为什么我觉得应该叫它上界。。难道它指的不是经过国家的最大疫情值吗?不过这个下界也可能指的是最劣情况的意思)。不过疫情值并不是真的时间,搜索顺序可以任意,所以可以和暴力搜索一样按照疫情值从大到小考虑作为优化。

关于代码:我上网搜了一下才想起来了int &的用法,https://blog.csdn.net/willian0621/article/details/12838157

所以swap(u, v)不仅交换了u和v还交换了e[i].u和e[i].v。还有加入双向边的时候我想还是应该判断一下的,不过在这里不判断也没有关系因为把L < R传进去肯定没有符合要求的区间,所以不造成任何修改,和“三级跳”那个题不一样的是那个题有pushup,进不去的话会导致叶子被它不存在的孩子更新为空。

我发现我鹤的代码里的merge函数和某些运算符重载始终没有用到,于是就把它们删掉了,关于国家的结构体莫名其妙的变成了city这件事……

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 4e5 + 5;
const int inf = 1e9 + 7;

int n, m, ls[maxn+maxn], f[maxn], siz[maxn], val[maxn], ans[maxn];
bool vis[maxn];
vector<int> sol[maxn+maxn];

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

int fa(int x) {return f[x] == x ? x : fa(f[x]);}

struct edge 
{
    int u, v;
}e[maxn];

struct city//为什么国家变成了城市。。
{
    int c, t, s, id;
}d[maxn];

struct sta
{
    bool dir;
    int fx, x, valfx, tim;
}st[maxn];
int top;

void add_edge(int tim, int id, bool dir)
{
    if(dir)
    {
        int u = e[id].u, v = e[id].v;
        u = fa(u), v = fa(v);
        if(val[u] < ans[v])
        {
            ++top; st[top].tim = tim;
            st[top].dir = dir;
            st[top].x = st[top].fx = u;
            st[top].valfx = val[u];
            val[u] = ans[v];
        }
    }
    else 
    {
        int u = e[id].u, v = e[id].v;
        u = fa(u), v = fa(v);
        if(u == v) return;
        if(siz[u] < siz[v]) swap(u, v);

        ++top;
        st[top].x = v;
        st[top].fx = u;
        st[top].valfx = val[u];
        st[top].dir = dir;
        st[top].tim = tim;

        f[v] = u;
        val[u] = max(val[u], val[v]);
        siz[u] += siz[v];
    }
}

void del(int tim)
{
    while(top && st[top].tim == tim)
    {
        if(st[top].dir)
        {
            val[st[top].x] = st[top].valfx;
        }
        else 
        {
            int u = st[top].fx, v = st[top].x;
            siz[u] -= siz[v];
            f[v] = v;
            val[u] = st[top].valfx;
        }
        --top;
    }
}

struct seg 
{
    struct node 
    {
        vector<int> dir, nir;
    }t[maxn<<2];
    void insert(int x, int l, int r, int L, int R, int id, bool dir)
    {
        if(L <= l && r <= R)
        {
            if(dir) t[x].dir.push_back(id);
            else t[x].nir.push_back(id);
            return;
        }
        int mid = (l + r) >> 1;
        if(L <= mid) insert(x<<1, l, mid, L, R, id, dir);
        if(R > mid) insert(x<<1|1, mid+1, r, L, R, id, dir);
    }
    void solve(int x, int l, int r)
    {
        for(int v : t[x].dir) add_edge(x, v, 1);
        for(int v : t[x].nir) add_edge(x, v, 0);
        if(l == r)
        {
            for(int v : sol[l]) ans[v] = max(ans[v], val[fa(v)]);
            del(x);
            return;
        }
        int mid = (l + r) >> 1;
        solve(x<<1|1, mid+1, r);
        solve(x<<1, l, mid);
        del(x);
    }
}t;

int main()
{
    n = read(); m = read();
    for(int i=1; i<=n; i++) d[i].c = read(), d[i].t = read(), d[i].s = read();
    for(int i=1; i<=m; i++) e[i].u = read(), e[i].v = read();
    int cnt = 0;
    for(int i=1; i<=n; i++) ls[++cnt] = d[i].c, ls[++cnt] = d[i].t;
    sort(ls+1, ls+cnt+1); cnt = unique(ls+1, ls+cnt+1)-ls-1;
    for(int i=1; i<=n; i++) d[i].c = lower_bound(ls+1, ls+cnt+1, d[i].c)-ls;
    for(int i=1; i<=n; i++) d[i].t = lower_bound(ls+1, ls+cnt+1, d[i].t)-ls;
    for(int i=1; i<=n; i++) sol[d[i].c].push_back(i);
    for(int i=1; i<=m; i++)
    {
        int &u = e[i].u, &v = e[i].v;
        if(d[u].c > d[v].c) swap(u, v);
        if(d[v].c <= min(d[v].t, d[u].t)) t.insert(1, 1, cnt, d[v].c, min(d[v].t, d[u].t), i, 0);
        if(d[u].c <= min(d[v].c-1, d[u].t)) t.insert(1, 1, cnt, d[u].c, min(d[v].c-1, d[u].t), i, 1);
    }
    for(int i=1; i<=n; i++) f[i] = i, siz[i] = 1, val[i] = d[i].s, ans[i] = d[i].s;
    t.solve(1, 1, cnt);
    for(int i=1; i<=n; i++) printf("%d ", ans[i]);

    return 0;
}
%%%Chen_jr

 

D. 暴力题

4分?!我以为应该有5分的……难道是因为没用逆元??

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e5 + 6;
const ll mod = 998244353;

int n, k, w, a[maxn], q;
ll ans;
multiset<int> s;

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

ll qpow(ll a, ll b)
{
    ll ans = 1;
    while(b)
    {
        if(b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}

int main()
{
    n = read(); k = read(); w = read();
    for(int i=1; i<=n; i++) 
    {
        a[i] = read(); s.insert(a[i]);
    }
    q = read();
    while(q--)
    {
        int pos = read(), x = read();
        s.erase(s.find(a[pos]));
        a[pos] = x;
        s.insert(a[pos]);
        int i = 1; ans = 0;
        for(int y : s)
        {
            ll ad = (double)y/w*qpow(i, k); ad %= mod;
            ans = (ans + ad)%mod;
            i++;
        }
        printf("%lld\n", ans);
    }

    return 0;
}
View Code

 

posted @ 2022-10-12 17:30  Catherine_leah  阅读(64)  评论(0编辑  收藏  举报
/* */