2021“MINIEYE杯”中國大學生算法設計超級聯賽(10)


2021“MINIEYE杯”中國大學生算法設計超級聯賽(10)

慶祝暑期訓練賽結束了

Pty loves lines

  • 題意

\(n\)條直線,求直線相交的所有可能的交點數情況並輸出。

  • 思路

首先,每條直線最極端情況(所有直線不平行),那么就有\(\frac{(n * (n - 1))}{2}\)個交點。
之后,我們先管直線的平行情況。

  1. 所有直線相交,即0(所有\(n\)都存在的一種情況)
  2. \(n - 1\)條直線平行,即\(1 * (n - 1) = n - 1\),一種
  3. \(n - 2\)條直線平行,那么另外兩條直線可以平行,也可以相交,那么答案為: \(2 * (n - 2) + 0\ or\ 2 * (n - 2) + 1\)兩種。
  4. \(n - 3\)條直線平行,那么看另外三條直線\((0,2,3) + (3 * (n - 3))\)
    之后其實都差不多,那么我們也可以發現第四種情況的時候,看另外三條直線的情況,其實左邊那部分就是\(n = 3\)時的情況,后面在推也一樣,那么通過這個性質就可以求遞推公式,然后用\(dp\)進行狀態轉移即可。
    定義\(f[i][j]\)表示\(i\)條直線,\(j\)個交點的情況是否可以,(\(f[i][j]\)為1或0),那么\(f[i][j] = f[i - k][j + (k * (i - k))]\),就類似我上面的遞推,然后\(O(n^4)\)打表程序就出來了0.0,其實我們發現這個性質后就應該想到可以用bitset加一層優化,然后通過打表可以發現,一大段數字在后面都是連續的不用管(最多在30000多那就一直連續了,這個連續的最大值也可以打表求出來),那么就可以直接暴力點不管那后面的,那么就可以隨便跑了\(O(n * n * 32000 / w)\)

code :

const int N = 710, M = 32000;

bitset<M> f[N];

void init() {
    f[0][0] = 1;
    fep(i,1,700) {
        fep(k,1,i) {
            f[i] |= f[i - k] << (k * (i - k));
        }
    }
}

void solve(){
    int n;
    cin >> n;
    int limit = min(M - 1, n * (n - 1) / 2);
    bool flag = 0;
    for(int i = 0;i <= limit;i ++){
        if(f[n][i]) {
            if(flag) cout << ' ';
            else flag = 1; 
            cout << i;
        } 
    }  
    for(int i = limit + 1;i <= (n * (n - 1) / 2);i ++) {
        cout << ' ' << i;
    }
    cout << endl;
}

Pty loves string

  • 題意

給一個字符串\(S\),共\(Q\)次詢問,問字符串\(S\)中長度為\(l\)的前綴 + 長度為\(r\)的后綴形成的字符串在\(S\)中出現的次數。

  • 思路

\(kmp\)找到字符串中長度相同的子串,前后都跑一邊,那么長度為\(l\),到某個點\(x\)\(1 \to l = x \to x + l - 1\),點y,\(n - r \to n = y - r + 1 \to y\),然后就是判斷\(y - r + 1 = x + l\)的點即可,然后就是找一點\(z\)\(1 \to l = z - l + 1 \to z\ and\ n - r \to n = z \to z + r - 1\),存在即答案加1,可以看出建圖跑比較方便。
然后建兩棵樹的圖即可,隨意用一顆樹的dfs序在另一顆樹上跑主席樹,可持久化記憶。然后每次查詢即可

code :

int in[N][2], out[N][2], tot[2], pos[N];
int rt[N];
int ls[N << 8],rs[N << 8],sum[N << 8], idx;
char str[N];
int f1[N],f2[N];
int kkk;
int h1[N],e1[N],ne1[N],m1, h2[N], e2[N],ne2[N], m2;

void ad1(int a,int b) {
    e1[m1] = b, ne1[m1] = h1[a], h1[a] = m1 ++;
}

void ad2(int a,int b) {
    e2[m2] = b, ne2[m2] = h2[a], h2[a] = m2 ++;
}
void dfs1(int u)
{
    in[u][0] = ++ tot[0];
    pos[tot[0]] = u;
    for(int i = h1[u];~i;i = ne1[i]) {
        int j = e1[i];
        dfs1(j);
    }
    out[u][0] = tot[0];
}
void dfs2(int u) {
    in[u][1] = ++ tot[1];
    for(int i = h2[u];~i;i = ne2[i]) {
        int j = e2[i];
        dfs2(j);
    }
    out[u][1] = tot[1];
}

void insert(int &t,int pre,int l,int r,int x) {
    t = ++ idx;
    ls[t] = ls[pre]; rs[t] = rs[pre];
    sum[t] = sum[pre] + 1;
    if(l == r) {return;}
    int mid = l + r >> 1;
    if(x <= mid) insert(ls[t],ls[pre],l,mid,x);
    else insert(rs[t],rs[pre],mid + 1,r,x);
}

int query(int p,int q,int l,int r,int x,int y) {
    if(r < x || y < l) return 0;
    if(x <= l && r <= y) return sum[p] - sum[q];
    int mid = l + r >> 1;
    return query(ls[p],ls[q],l,mid,x,y) + query(rs[p],rs[q],mid + 1,r,x,y);
}

void solve(){
    int n,q;
    cin >> n >> q;
    cin >> (str + 1);
    // 字符串kmp預處理
    int j = 0;
    memset(h1,-1,sizeof h1);
    memset(h2,-1,sizeof h2);
    for(int i = 2;i <= n;i ++) {
        while(j && str[j + 1] != str[i]) j = f1[j];
        if(str[j + 1] == str[i]) j ++;
        f1[i] = j;
    }
    j = n + 1;
    f2[n] = n + 1;
    for(int i = n - 1;i >= 1;i --) {
        while(j <= n && str[j - 1] != str[i]) j = f2[j];
        if(str[j - 1] == str[i]) j --;
        f2[i] = j;
    }
    // 建樹
    for(int i = 1;i <= n;i ++) {
        ad1(f1[i],i);
        ad2(f2[i],i);
    }
    dfs1(0);
    dfs2(n + 1);
    // 處理
    for(int i = 1;i <= n + 1;i ++) { // 注意我們的dfs序是從0或n + 1開始的,所以需要加1
        insert(rt[i], rt[i - 1],1,n + 1,in[pos[i] + 1][1]);
    }
    while(q --) {
        int x,y;
        cin >> x >> y;
        y = n - y + 1;
        cout << query(rt[out[x][0]],rt[in[x][0] - 1],1,n + 1,in[y][1],out[y][1]) << endl;
    }
    for(int i = 0;i <= n + 1;i ++) f1[i] = f2[i] = 0, rt[i] = 0;
    idx = tot[1] = tot[0] = 0;
    m1 = m2 = 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM