LibreOJ NOIP Round #1

LibreOJ NOIP Round #1


兩道題花式爆炸…怕是連省一都拿不到了QwQ

D1T1:DNA序列

給定一個由AGCT組成的字符串,問長度爲k的子串出現次數最多的出現了幾次。

Sol:將字符串一一映射到[0,4n] 的數上,用個桶統計一下答案。映射可以用哈希+自然溢出。

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 5000006;
unsigned long long bas = 4;
typedef unsigned long long ull;

int n, k;
char str[MAXN];
ull hash_val[MAXN], val = 1;
int st[1<<22|1];

int main()
{
    scanf("%s", str+1);
    n = strlen(str+1);
    scanf("%d", &k);
    for (register int i = 1; i <= k; i++) val *= bas;
    for (register int i = 1; i <= n; i++) {
        hash_val[i] = hash_val[i-1]*bas;
        if (str[i] == 'C') hash_val[i]++;
        if (str[i] == 'T') hash_val[i]+=2;
        if (str[i] == 'G') hash_val[i]+=3;
    }
    int dat = 0;
    for (register int i = 1; i+k-1 <= n; i++)
        dat = max(dat, ++st[hash_val[i+k-1]-hash_val[i-1]*val]);
    printf("%d\n", dat);
    return 0;
}

D1T2:數列遞推

給定集合S 和若干個數列an=kan1+an2 ,問S 中使得Sai,iS 取最大/最小的ai

顯然這個數列指數遞減,向後試探若干項並計算貢獻即可。

注意特判全爲0!!!

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 100005;

int s[MAXN], n, m, k;
long long f[1005];

int main()
{
    scanf("%d", &m);
    for (int i = 1; i <= m; i++) scanf("%d", &s[i]);
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%lld%lld%d", &f[0], &f[1], &k);
        if (f[0] == 0 && f[1] == 0) { printf("%d %d\n", s[1], s[1]); continue;}
            int max_pos = -1, min_pos = -1;
        int cur = 0, j = 1;
        while (j <= m) {
            while (j <= m && s[j] <= cur) {
                if (min_pos == -1 || f[s[j]] < f[min_pos]) min_pos = s[j];
                if (max_pos == -1 || f[s[j]] > f[max_pos]) {
                    max_pos = s[j];
                }
                j++;
            }
            if (cur && f[cur-1] > 0 && f[cur] > f[cur-1]) {
                if (max_pos == -1) max_pos = s[m], min_pos = s[1];
                else {
                    register int p = cur+1;
                    while (f[p] <= f[max_pos]) {
                        f[p+1] = k*f[p]+f[p-1];
                        p++;
                    }
                    if (s[m] >= p) max_pos = s[m];
                    if (j <= m && s[j] < p && f[s[j]] < f[min_pos]) min_pos = s[j];
                }
                break;
            }
            if (cur && f[cur-1] < 0 && f[cur] < f[cur-1]) {
                if (max_pos == -1) max_pos = s[1], min_pos = s[m];
                else {
                    register int p = cur+1;
                    while (f[p] >= f[min_pos]) {
                        f[p+1] = k*f[p]+f[p-1];
                        p++;
                    }
                    if (s[m] >= p) min_pos = s[m];
                    if (j <= m && s[j] < p && f[s[j]] > f[max_pos]) max_pos = s[j];
                }
                break;
            }
            f[cur+2] = k*f[cur+1]+f[cur];
            cur++;
        }
        printf("%d %d\n", max_pos, min_pos);
    }
    return 0;
}

D1T3:旅遊路線

考慮dp。設dis[i][j] 爲從i 開始到j ,經過不超過c[i] 條邊,最長的距離。這個dp可以倍增的計算。

dp[i][j]i 點錢爲j 最遠的旅行,則:

dp[i][j]=max{dp[k][jp[i]]+dis[i][k]}

詢問可以二分答案做。

複雜度O(n4+n3logC+Tlogp)

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 105;

int g[MAXN][MAXN][21], dis[2][MAXN][MAXN];
int dp[MAXN][MAXN*MAXN];

int n, m, C, T;
int p[MAXN], c[MAXN];

int main()
{
    scanf("%d%d%d%d", &n, &m, &C, &T);
    for (int i = 1; i <= n; i++) scanf("%d%d", &p[i], &c[i]), c[i] = min(c[i], C);
    memset(g, -127/3, sizeof g);
    for (int i = 1; i <= m; i++) {
        int u, v, d; scanf("%d%d%d", &u, &v, &d);
        g[u][v][0] = d;
    }
    for (int i = 1; i <= n; i++) g[i][i][0] = 0;
    for (int k = 1; k <= 20; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++) {
                g[i][j][k] = g[i][j][k-1];
                for (int t = 1; t <= n; t++)
                    g[i][j][k] = max(g[i][t][k-1]+g[t][j][k-1], g[i][j][k]);
                // cerr << i << " " << j << " " << k << " " << g[i][j][k] << endl;
            }
    memset(dis, -127/3, sizeof dis);
    for (int i = 1; i <= n; i++) {
        int pre = 0, now = 1;
        dis[pre][i][i] = dis[now][i][i] = 0;
            for (int k = 0; k <= 20; k++)
            if (c[i]&(1<<k)) {
                for (int j = 1; j <= n; j++)
                    for (int t = 1; t <= n; t++) {
                        dis[now][i][j] = max(dis[now][i][j], dis[pre][i][t]+g[t][j][k]);
                    }
                swap(now, pre);
                memcpy(dis[now], dis[pre], sizeof dis[pre]);
            }
        if (pre == 1) memcpy(dis[0], dis[1], sizeof dis[1]);
    }
    memset(dp, 0, sizeof dp);
    for (register int j = 1; j <= n*n; j++) {
        for (register int i = 1; i <= n; i++) {
            if (p[i] <= j)
                for (register int k = 1; k <= n; k++)
                    dp[i][j] = max(dp[i][j], dp[k][j-p[i]]+dis[0][i][k]);
        }
    }
    for (int i = 1; i <= T; i++) {
        int s, q, d;
        scanf("%d%d%d", &s, &q, &d);
        if (dp[s][q] < d) puts("-1");
        else {
            int L = 1, R = q, mid;
            while (L <= R) {
                mid = (L+R)>>1;
                if (dp[s][mid] < d) L = mid+1;
                else R = mid-1;
            }
            printf("%d\n", q-L);
        }
    }
    return 0;
}

D2T1:遊戲

給定n2×106 ,要求構造一個無向圖,使得三元環個數爲n|V|500

考慮先做一個儘可能大的團,然後貪心給新節點與團中的節點連儘可能多的邊。

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 505;

int n, val = 3, x = 0;

int g[MAXN][MAXN];

int main()
{
    scanf("%d", &n);
    while (val*(val-1)*(val-2)/6 <= n)
        val++;
    val--;
    n -= val*(val-1)*(val-2)/6;
    x = val;
    for (int i = 1; i <= val; i++)
        for (int j = 1; j < i; j++)
            g[i][j] = g[j][i] = 1;
    for (int i = val; i >= 2; i--) {
        if (n >= i*(i-1)/2) {
            n -= i*(i-1)/2;
            ++x;
            for (int j = 1; j <= i; j++) g[j][x] = g[x][j] = 1;
        }
    }
        while (n) {
        ++x;
        g[1][x] = g[x][1] = g[2][x] = g[x][2] = 1;
        n--;
    }
    printf("%d\n", x);
    for (int i = 1; i < x; i++) {
        for (int j = i+1; j <= x; j++)
            printf("%d ", g[i][j]);
        puts("");
    }
    return 0;
}

D2T2:七曜聖賢

題意較長orz.

如果沒有修改,則很容易用一個並查集維護mex。如果有修改,則並查集維護的不再是答案,而是答案的上限。考慮修改的影響,扔出一個p[i] 會使得答案p[i] 。同時多次扔出是具有單調性的(這次扔出的一定在上一次扔出的撿回後撿回,所以這次扔出的比上次小,則上次就沒有貢獻了)。可以用單調隊列維護之。

#include <bits/stdc++.h>
using namespace std;

namespace IO{
    int c;
    unsigned int seed;
    unsigned int randnum(){
        seed^=seed<<13;
        seed^=seed>>17;
        seed^=seed<<5;
        return seed;
    }

    inline int read(int &x){scanf("%d",&x);return x;}
    inline void init_case(int &m,int &a,int &b,int &d,int p[]){
        scanf("%d%u%d%d%d%d",&m,&seed,&a,&b,&c,&d);
        for(int i=1;i<=m;i++){
            if(randnum()%c==0)p[i]=-1;
            else p[i]=randnum()%b;
        }
    }

    inline void update_ans(unsigned int &ans_sum,unsigned int cur_ans,int no){
        const static unsigned int mod=998244353;
        ans_sum^=(long long)no*(no+7)%mod*cur_ans%mod;
    }
}
using IO::read;
using IO::init_case;
using IO::update_ans;

const int MAXN = 2000005;
int m, a, b, d, p[MAXN];
int T;
bitset<MAXN*2> bt, inside;

unsigned int ans_sum = 0, cur_ans = 0;
int max_ans = 0;

int que[MAXN];
int l = 1, r = 0;
int q[MAXN], ql = 1, qr = 0; // 普通隊列

int fa[MAXN];
inline int findf(int nd)
{ return fa[nd]?fa[nd]=findf(fa[nd]):nd; }
int tot = 0;

int main()
{
    read(T);
    while (T--) {
        init_case(m, a, b, d, p);
        bt.reset(), inside.reset();
        memset(fa, 0, sizeof fa);
        cur_ans = ans_sum = 0;
        l = 1, r = 0, ql = 1, qr = 0;
        for (register int i = 0; i <= a; i++) bt[i] = inside[i] = 1, fa[i] = a+1;
        for (register int i = 1; i <= m; i++) {
            if (d == 1) {
                if (p[i] != -1 && !bt[p[i]]) {
                    bt[p[i]] = inside[p[i]] = 1;
                    fa[p[i]] = findf(p[i]+1);
                    cur_ans = findf(0);
                    update_ans(ans_sum, cur_ans, i);
                }
            } else {
                if (p[i] != -1 && !bt[p[i]]) {
                    bt[p[i]] = inside[p[i]] = 1;
                    fa[p[i]] = findf(p[i]+1);
                } else if (p[i] != -1 && inside[p[i]]) {
                    while (l <= r && p[i] < que[r]) r--;
                    que[++r] = p[i];
                    q[++qr] = p[i];
                    inside[p[i]] = 0;
                } else if (ql <= qr) {
                    if (q[ql] == que[l]) l++;
                    inside[q[ql++]] = 1;
                } else {continue;}
                cur_ans = findf(0);
                if (l <= r) cur_ans = min(cur_ans, (unsigned int)que[l]);
                update_ans(ans_sum, cur_ans, i);
            }
        }
        printf("%u\n", ans_sum);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章