• 2019 Multi-University Training Contest 1


    2019 Multi-University Training Contest 1

    题目链接

    Blank

    题目要求只能放四个数,并且对于每个区间而言,统计个数时会发现只有最后一个位置有贡献,所以考虑(dp(i,j,k,t,p))表示前(i)个字符,四个数的最后一个位置从小到大为(j,k,t,p),之后就对于每个在(i)结束的区间,检验状态的合法性就行了。
    这里有两个优化,一个就是(i)肯定会和后面的四个之一相等,那么就可以减掉一维;另一个就是还可以考虑一下滚动数组。
    细节见代码吧:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 105, MOD = 998244353;
    int T, n, m;
    int dp[2][N][N][N];
    struct Seg{
        int l, r, x;
    }s[N];
    int add(int &x, int y) {
        return (x += y) >= MOD ? x - MOD : x;
    }
    vector <int> v[N];
    int cnt[5];
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        while(T--) {
            cin >> n >> m;
            memset(dp, 0, sizeof(dp));
            dp[0][0][0][0] = 1;
            for(int i = 1; i <= n; i++) v[i].clear();
            for(int i = 1; i <= m; i++) {
                cin >> s[i].l >> s[i].r >> s[i].x;
                v[s[i].r].push_back(i);
            }
            int cur = 0;
            for(int d = 0; d < n; d++) {
                for(int a = 0; a <= d; a++) {
                    for(int b = a; b <= d; b++) {
                        for(int c = b; c <= d; c++) {
                            dp[cur ^ 1][a][b][d] = add(dp[cur ^ 1][a][b][d], dp[cur][a][b][c]);
                            dp[cur ^ 1][a][c][d] = add(dp[cur ^ 1][a][c][d], dp[cur][a][b][c]);
                            dp[cur ^ 1][b][c][d] = add(dp[cur ^ 1][b][c][d], dp[cur][a][b][c]);
                            dp[cur ^ 1][a][b][c] = add(dp[cur ^ 1][a][b][c], dp[cur][a][b][c]);
                        }
                    }
                }
                for(int a = 0; a <= d; a++) for(int b = a; b <= d; b++) for(int c = b; c <= d; c++) {
                    dp[cur][a][b][c] = 0;
                    for(auto i : v[d + 1]) {
                        if(1 + (a >= s[i].l) + (b >= s[i].l) + (d >= s[i].l) != s[i].x) dp[cur ^ 1][a][b][d] = 0;
                        if(1 + (a >= s[i].l) + (c >= s[i].l) + (d >= s[i].l) != s[i].x) dp[cur ^ 1][a][c][d] = 0;
                        if(1 + (b >= s[i].l) + (c >= s[i].l) + (d >= s[i].l) != s[i].x) dp[cur ^ 1][b][c][d] = 0;
                        if(1 + (a >= s[i].l) + (b >= s[i].l) + (c >= s[i].l) != s[i].x) dp[cur ^ 1][a][b][c] = 0;
                    }
                }
                cur ^= 1;
            }
            int ans = 0;
            for(int a = 0; a <= n; a++) {
                for(int b = a; b <= n; b++) {
                    for(int c = b; c <= n; c++) {
                        ans = add(ans, dp[cur][a][b][c]);
                    }
                }
            }
            cout << ans << '
    ';
        }
        return 0;
    }
    

    Operation

    这个题要求强制在线,所以思考怎么快速求出区间的线性基来搞,因为求区间异或最大值一般而言都会求出区间的线性基。
    显然暴力是不行的,但是通过一些可持久算法我们能得到一些启发,就是维护一个前缀线性基,注意维护线性基时注意贪心选基的位置,即每个基尽量选在右边的。
    详见代码:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e6 + 5;
    int T, n, m;
    int v[N][32], pos[N][32];
    void insert(int p, int val) {
        for(int i = 0; i <= 31; i++) v[p][i] = v[p - 1][i], pos[p][i] = pos[p - 1][i];
        int k = p;
        for(int i = 31; i >= 0; i--) {
            if(val >> i & 1) {
                if(!v[p][i]) {
                    v[p][i] = val;
                    pos[p][i] = k;
                    return;
                }
                if(pos[p][i] < k) {
                    swap(k, pos[p][i]);swap(val, v[p][i]);
                }
                val ^= v[p][i];
            }
        }
    }
    int query(int l, int r) {
        int ans = 0;
        for(int i = 31; i >= 0; i--) {
            if(pos[r][i] >= l && (ans ^ v[r][i]) > ans) ans ^= v[r][i];
        }
        return ans;
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        while(T--) {
            cin >> n >> m;
            for(int i = 1, x; i <= n; i++) {
                cin >> x; insert(i, x);
            }
            int ans = 0;
            while(m--) {
                int op; cin >> op;
                if(op) {
                    int x; cin >> x; x ^= ans;
                    insert(++n, x);
                } else {
                    int l, r; cin >> l >> r;
                    l = (l ^ ans) % n + 1, r = (r ^ ans) % n + 1;
                    if(l > r) swap(l, r);
                    cout << (ans = query(l, r)) << '
    ';
                }
            }
        }
        return 0;
    }
    

    Vacation

    做法有点多:单调队列模拟、二分时间等等。
    但这里有个线性的做法,我们会发现最后一定是一堆车在一起通过终点的,我们就枚举最后有多少个车在一起通过终点,然后计算时间,取(max)即可。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5;
    int n;
    int l[N], s[N], v[N];
    int main() {
        while(scanf("%d", &n) != EOF) {
            for(int i = 0; i <= n; i++) scanf("%d", &l[i]);
            for(int i = 0; i <= n; i++) scanf("%d", &s[i]);
            for(int i = 0; i <= n; i++) scanf("%d", &v[i]);
            ll sum = 0;
            double ans = (double)s[0] / v[0];
            for(int i = 1; i <= n; i++) {
                ans = max(ans, (double)((sum += l[i]) + s[i]) / v[i]);
            }
            printf("%.10f
    ", ans);
        }
        return 0;
    }
    

    Path

    求出最短路图,然后求最小割即可。
    代码如下:

    Code
    #include <bits/stdc++.h>
    #define INF 0x3f3f3f3f3f3f3f3f
    #define t n
    using namespace std;
    typedef long long ll;
    const int N = 1e4 + 5;
    int T, n, m;
    struct Edge{
        int v, w, next;
    }e[N << 1];
    struct edge{
        int u, v, w;
    }E[N];
    struct node{
        ll d;
        int u;
        bool operator < (const node &A)const {
            return d > A.d;
        }
    };
    bool vis[N];
    int head[N], tot;
    void adde(int u, int v, int w) {
        e[tot].v = v; e[tot].w = w; e[tot].next = head[u]; head[u] = tot++;
    }
    ll d[N], d2[N];
    void Dij(int x) {
        priority_queue <node> q;
        memset(vis, 0, sizeof(vis));
        memset(d, INF, sizeof(d)) ;d[x] = 0;
        q.push(node{0, x});
        while(!q.empty()) {
            node now = q.top(); q.pop();
            int u = now.u;
            if(vis[u]) continue ;
            vis[u] = 1;
            for(int i = head[u]; i != -1; i = e[i].next) {
                int v = e[i].v;
                if(d[v] > now.d + e[i].w) {
                    d[v] = now.d + e[i].w;
                    q.push(node{d[v], v});
                }
            }
        }
    }
    void Dij2(int x) {
        priority_queue <node> q;
        memset(vis, 0, sizeof(vis));
        memset(d2, INF, sizeof(d2)) ;d2[x] = 0;
        q.push(node{0, x});
        while(!q.empty()) {
            node now = q.top(); q.pop();
            int u = now.u;
            if(vis[u]) continue ;
            vis[u] = 1;
            for(int i = head[u]; i != -1; i = e[i].next) {
                int v = e[i].v;
                if(d2[v] > now.d + e[i].w) {
                    d2[v] = now.d + e[i].w;
                    q.push(node{d2[v], v});
                }
            }
        }
    }
    void adde2(int u, int v, int w) {
        e[tot].v = v; e[tot].w = w; e[tot].next = head[u]; head[u] = tot++;
        e[tot].v = u; e[tot].w = 0; e[tot].next = head[v]; head[v] = tot++;
    }
    bool bfs(int S,int T){
        memset(d,0,sizeof(d));d[S]=1;
        queue <int > q;q.push(S);
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=head[u];i!=-1;i=e[i].next){
                int v=e[i].v;
                if(!d[v] && e[i].w>0){
                    d[v]=d[u]+1;
                    q.push(v);
                }
            }
        }
        return d[T]!=0;
    }
    ll dfs(int S,int a){
        ll flow=0,f;
        if(S==t || a==0) return a;
        for(int i=head[S];i!=-1;i=e[i].next){
            int v=e[i].v;
            if(d[v]!=d[S]+1) continue ;
            f=dfs(v,min(a,e[i].w));
            if(f){
                e[i].w-=f;
                e[i^1].w+=f;
                flow+=f;
                a-=f;
                if(a==0) break;
            }
        }
        if(!flow) d[S]=-1;
        return flow;
    }
    int Dinic(){
        int max_flow=0;
        while(bfs(1,t)) max_flow+=dfs(1,INF);
        return max_flow;
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T;
        while(T--) {
            memset(head, -1, sizeof(head)); tot = 0;
            cin >> n >> m;
            for(int i = 1; i <= m; i++) {
                int x, y, c; cin >> x >> y >> c;
                E[i] = {x, y, c};
                adde(y, x, c);
            }
            Dij(n);
            if(d[1] == INF) {
                cout << 0 << '
    ';
                continue ;
            }
            memset(head, -1, sizeof(head)); tot = 0;
            for(int i = 1; i <= m; i++) adde(E[i].u, E[i].v, E[i].w);
            Dij2(1);
            memset(head, -1, sizeof(head)); tot = 0;
            for(int i = 1; i <= m; i++) {
                int u = E[i].u, v = E[i].v;
                if(d2[u] + d[v] + E[i].w == d[1]) {
                    adde2(u, v, E[i].w);
                }
            }
            cout << Dinic() << '
    ';
        }
        return 0;
    }
    

    String

    贪心构造即可,然后每次贪心选择后,check一下剩下的可不可以满足条件。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5, MAX = 26;
    char s[N];
    int n, k;
    int L[MAX], R[MAX], used[MAX], cnt[MAX];
    int suf[N][MAX];
    vector <char> ans;
    vector <int> v[MAX];
    bool ok(int x, int p) {
        int sum1 = 0, sum2 = 0;
        for(int i = 0; i < MAX; i++) {
            int pl = max(L[i] - used[i], 0), pr = min(R[i] - used[i], suf[n][i] - suf[p][i]);
            if(pr < pl) return false;
            sum1 += pl; sum2 += pr;
        }
        return sum1 <= x && x <= sum2;
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        while(cin >> s + 1 >> k) {
            n = strlen(s + 1);
            for(int i = 0; i < MAX; i++) v[i].clear(), cnt[i] = used[i] = 0; ans.clear();
            for(int i = 1; i <= n; i++) v[s[i] - 'a'].push_back(i), cnt[s[i] - 'a']++;
            for(int i = 1; i <= n; i++) {
                for(int j = 0; j < MAX; j++) suf[i][j] = suf[i - 1][j];
                suf[i][s[i] - 'a']++;
            }
            for(int i = 0; i < MAX; i++) cin >> L[i] >> R[i];
            int last = 0, f = 1;
            for(int i = 1; i <= k && f; i++) {
                for(int j = 0; j < MAX; j++) {
                    int pos = lower_bound(v[j].begin(), v[j].end(), last + 1) - v[j].begin();
                    int SZ = (int)v[j].size();
                    used[j]++;
                    if(SZ == pos || !ok(k - i, v[j][pos])) {
                        used[j]--;
                        if(j == MAX - 1) {
                            cout << -1; f = 0; break;
                        }
                        continue ;
                    }
                    last = v[j][pos]; cnt[j]--;
                    ans.push_back(j + 'a');
                    break;
                }
            }
            for(auto t : ans) cout << t;
            cout << '
    ';
        }
        return 0;
    }
    

    Function

    化简式子即可,感觉题解还是挺清楚的了。。(其实是我懒得再照着打一次了= =)
    注意一下上界吧,感觉数论题细节还是挺多的。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e7 + 5, MOD = 998244353;
    template <class T>
    void read(T &x) {
        static char ch; static bool neg;
        for(ch = neg = 0; ch < '0' || ch > '9'; neg |= (ch == '-'), ch = getchar());
        for(x = 0; ch >= '0' && ch <= '9'; x = x * 10 + ch - '0', ch = getchar());
        x = neg ? -x : x;
    }
    
    int Add(int x, int const &y) {
        return (x += y) >= MOD ? x - MOD : x;
    }
    int Mul(long long x, int const &y) {
        return x * y % MOD;
    }
    int v[N], prime[N], phi[N], tot;
    void pre() {
        phi[1] = 1;
        for(int i = 2; i <= N - 1; i++) {
            if(!v[i]) {
                v[i] = i;
                prime[++tot] = i, phi[i] = i - 1;
            }
            for(int j = 1; j <= tot; j++) {
                if(prime[j] > v[i] || prime[j] > (N - 1) / i) break ;
                v[i * prime[j]] = prime[j];
                phi[i * prime[j]] = phi[i] * (i % prime[j] ? prime[j] - 1 : prime[j]);
            }
        }
    }
    __int128 n;
    int T;
    int calc(int k, __int128 L, __int128 R) {
        int ans = 0;
        for(int i = 1; 1ll * i * i <= k; i++) {
            if(k % i == 0) {
                ans = Add(ans, Mul((R / i - L / i) % MOD, phi[i]));
                if(k / i != i)
                    ans = Add(ans, Mul((R / (k / i) - L / (k / i)) % MOD, phi[k / i]));
            }
        }
        return ans;
    }
    int Sum1(long long x) {
        return x * (x + 1) / 2 % MOD;
    }
    int Sum2(long long x) {
        return x * (x + 1) % (6LL * MOD) * (2 * x + 1) / 6 % MOD;
    }
    int main() {
        pre();
        read(T);
        while(T--) {
            read(n);
            if(n <= 7) {
                printf("%d
    ", (int)n);
                continue ;
            }
            int r;
            for(r = 1; (__int128)(r + 2) * (r + 2) * (r + 2) - 1 <= n; r++);
            int ans = calc(r + 1, (__int128)(r + 1) * (r + 1) * (r + 1) - 1, n);
            for(int i = 1; i <= r; i++) {
                ans = Add(ans, Mul(Add(Add(Mul(3ll * i, Sum2(r / i)), 3ll * Sum1(r / i) % MOD), r / i), phi[i]));
            }
            printf("%d
    ", ans);
        }
        return 0;
    }
    
  • 相关阅读:
    《不生不熟》读后感 读书笔记
    《亚洲与一战》读后感 读书笔记
    《厨房》读后感 读书笔记
    《娇惯的心灵》读后感 读书笔记
    《实践理性批判》读后感 读书笔记
    嵌入式三级知识点整理
    C语言:输入一个数,输出比这个数小的所有素数,并求出个数。
    C语言知识点记录
    C语言-实现矩阵的转置-随机函数产生随机数并赋予数组中-190222
    将数字字符转为数字的两种方法。
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11284939.html
Copyright © 2020-2023  润新知