1. 程式人生 > >qbxt國慶水題記day4

qbxt國慶水題記day4

qbxt國慶水題記

day4

//0 + 20 + 0 == 20
//DP(不會) + 暴力 + 找規律(沒找到)

beautiful

1.1 題目
從前有一個序列 a[],對於每個 a[i] 都有一個在序列中的優美值,其定義是:序列中最長的
一段 [l,r], 滿足 l ≤ i ≤ r,且 a[i] 是這一段的中位數(以數值為第一關鍵字,下標為第二關鍵
字排序,這樣的話這一段的長度只有可能是奇數),r-l+1 就是它的優美值。
有 Q 個詢問,每次給出一段區間,求區間優美值的最大值

1.2 輸入
第一行輸入 n 接下來 n 個整數,代表 ai 接下來 Q,代表有 Q 個區間接下來 Q 行,每行
兩個整數 l, r(l <= r),表示區間的左右端點

1.3 輸出
對於每個區間的詢問,輸出答案

1.4 Sample Input
8
16 19 7 8 9 11 20 16
8
3 8
1 4
2 3
1 1
5 5
1 2
2 8
7 8

1.5 Sample Output
7
3
1
3
5
3
7
3

1.6 資料範圍৺㓖定
對於 30% 的資料滿足:1 ≤ n, Q ≤ 50
對於 70% 的資料滿足:1 ≤ n, Q ≤ 2000
對於 100% 的資料滿足,1 ≤ n ≤ 2000, 1 ≤ Q ≤ 100000, 1 ≤ ai ≤ 200

先預處理
以每個i為中心比a[i]大的為1,小的為0
記錄左右比a[i]大的為x( 1 - i<= x <= i - 1)的樹最靠邊的在那


從中間列舉看左右位置和是否為0

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn = 2000 + 10;
int n,a[maxn];
int l[maxn * 2],r[maxn * 2],cnt,w[maxn],f[maxn][maxn];

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 * 10 + ch - '0'; ch = getchar(); } return x * f; } int init() { for(int i = 1; i <= n; i++) { memset(l,255,sizeof(l)); l[n] = 0; memset(r,255,sizeof(r)); r[n] = 0; cnt = 0; for(int j = i - 1; j > 0; j--) { if(a[i] < a[j]) cnt++; if(a[i] >= a[j]) cnt--; l[n + cnt] = i - j; } cnt = 0; for(int j = i + 1; j <= n; j++) { if(a[i] <= a[j]) cnt++; if(a[i] > a[j]) cnt--; r[n + cnt] = j - i; } for(int j = 1 - i; j <= i - 1; j++) if(l[n + j] >= 0 && r[n - j] >= 0){ w[i] = max(w[i],l[n + j] + r[n - j] + 1); } } } int main() { freopen("beautiful.in","r",stdin); freopen("beautiful.out","w",stdout); n = read(); for(int i = 1; i <= n; i++) { a[i] = read(); } init(); for(int i = 1; i <= n; i++) { f[i][i] = w[i]; for(int j = i + 1;j <= n; j++) { f[i][j] = max(f[i][j-1],w[j]); } } int T = read(); while(T--) { int l = read(), r = read(); cout<<f[l][r]<<endl; } return 0; }

xor

2.1 題目᧿䘠
從前有一個長度為 n 的陣列 a[],求所有的 lowbit(AixorAj) 之和,其中 1 ≤ i ≤ n 且
1 ≤ j ≤ n
輸出答案對 998244353 取模
科普:
xor 指異或操作,在 C++ 中的運算子號為ˆ,運演算法則為:0 xor 0 = 0, 0 xor 1 = 1, 1 xor
0 = 1, 1 xor 1 = 0
lowbit(x) = 2k,其中 k 為最小的滿足 2k and x ̸= 0 的非負整數

2.2 輸入Ṭᔿ
第一行一個整數 T,表示資料組數
對於每組資料
第一行一個正整數 n,表示陣列長度
第二行 n 個非負整數,第 i 個整數為 Ai

2.3 輸出Ṭᔿ
每組資料輸出一行 Case #x: ans。x 表示組數編號,從 1 開始。ans 為所求值。

2.4 樣ֻ輸入輸出
2.5 Sample Input
2
5
4 0 2 7 0
5
2 6 5 4 0

2.6 Sample Output
Case #1: 36
Case #2: 40

2.7 資料範圍৺㓖定
所有的 Ai 都在 int 範圍內,所有資料滿足 1 ≤ T ≤ 10

測試點 n, m
1, 2, 3 ≤ 1000
4, 5, 6 ≤ 5000
7, 8, 9, 10 ≤ 5 ∗104

//寫過樹狀陣列竟然不知道lowbit(lowbit可以60)~~
lowbit

int lowbit(int x){
    return x&(-x);
}

正解是分治+tire樹的些思想
相當於建立了一個由01構成的trie樹
對於每一個節點的子節點左邊表示為1 右邊表示為0
數目相當於左子樹 * 右子樹
類似有歸併排序(當然trie也可以暴力做

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int mod = 998244353;
const int maxn = 50000 + 100;
int n,T;
long long sum;
int a[maxn];

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 * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

void dfs(int l ,int r, int k) {
    if(l >= r || k > 30) return;
    int p = l - 1;
    for(int i = l; i <= r; i++) {
        if(a[i] & (1<<k)) {
            p++;
            swap(a[i],a[p]);
        }
    }
    sum += (long long)(r - p) * (p - l + 1) % mod * (1<<k) % mod;
    sum %= mod;
    dfs(l,p,k + 1);
    dfs(p+1,r,k + 1);
}

void work(int t) {
    n = read();
    for(int i = 1; i <= n; i++) {
        a[i] = read();
    }
    sum = 0;
    dfs(1,n,0);
    sum = (sum * 2) % mod;
    printf("Case #%d: %d\n",t,sum);
}

int main() {
    freopen("xor.in","r",stdin);
    freopen("xor.out","w",stdout);
    T = read();
    for(int i = 1; i <= T; i++) {
        work(i);
    }
    return 0;
}

Permutation

3.1 題目᧿䘠
從前,有一個長度為 n 的排列 p
每次把排列的每個迴圈拿出來,寫成標準迴圈,再做一次排序
迴圈:排列第 i 位為 p[i],如果我們令 i 點到 p[i] 點連一條邊,那麼會形成若干個簡單環,
每個簡單環所有的 p[i] 就構成一個迴圈
比如排列 [4, 1, 6, 2, 5, 3],有 3 個迴圈 (421)(63)(5)
每個迴圈從任意一個位置開始讀都是一樣的
比如 (412) 也是 (124),(241)。n 個迴圈就一共 n 個表達法
我們規定一個標準迴圈是以迴圈內最大的數字開頭
迴圈之間排序的關鍵字就是第一個數字的大小
如 (421)(63)(5) 排序後是 (421)(5)(63)
如果排序後的排列和原排列一樣,那麼就是可行排列
求 n 個數的字典序第 k 大的可行排列

3.2 輸入Ṭᔿ
兩個整數,n,k
保證 k 在 long long 範圍內,保證有解

3.3 輸出Ṭᔿ
n 個整數,表示滿足條件的排列

3.4 樣ֻ輸入 1
4 3

3.5 樣ֻ輸出 1
1 3 2 4

3.6 樣ֻ解䟺 1
n=4 時,字典序最小的三個可行排列依次是
1 2 3 4
1 2 4 3
1 3 2 4

3.7 樣ֻ輸入 2
10 1
3.8 樣ֻ輸出 3
1 2 3 4 5 6 7 8 9 10

3.9 資料範圍的約定
對於 30% 的資料滿足:1 ≤ n ≤ 10
對於 100% 的資料滿足,1 ≤ n ≤ 50

奇葩的結論(竟然與裴波那契數列有關)
可以發現只有兩個緊挨著的數發生交換才為可行排列
以5為例
1 2 3 4 5 – 1
1 2 3 5 4 – 1 + 1
1 2 4 3 5 –
1 3 2 4 5 – 3 + 1
1 3 2 5 4 –
2 1 3 4 5 – 5 + 1
2 1 3 5 4 –
2 1 4 3 5 –
所以每次交換後有為第f[i]位(f[i]為裴波那契數列)
遞推下即可

程式碼

#include<iostream>
#include<cstdio>
using namespace std;

const int maxn = 100;
int a[maxn],n;
long long k,f[maxn];

void dfs(int val,long long k) {
    if(!k) return;
    for(int i = val; i < n;i++) {
        if(k > f[n - i]) {
            swap(a[i],a[i + 1]);
            dfs(val + 2, k - f[n - i]);
            return; 
        }
    }
}

int main() {
    freopen("Permutation.in", "r", stdin);
    freopen("Permutation.out", "w", stdout);
    cin>>n>>k;
    f[0] = 1, f[1] = 1;
    for(int i = 2; i <= n; i++) f[i] = f[i-1] + f[i-2];
    for(int i = 1; i <= n; i++) a[i] = i;
    dfs(1,k);
    for(int i = 1; i <= n; i++) cout<<a[i]<<' ';
    cout<<endl;
    return 0;
}