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 << '\n';
    }
    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)) << '\n';
            }
        }
    }
    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\n", 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 << '\n';
            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() << '\n';
    }
    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 << '\n';
    }
    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\n", (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\n", ans);
    }
    return 0;
}
posted @ 2019-08-01 19:56  heyuhhh  阅读(352)  评论(0编辑  收藏  举报