“卓見杯”第五屆CCPC中國大學生程序設計競賽河南省賽 部分題解

A 最大下降矩陣 < dp >

最長上升子序列的變形。
令f[i]表示以i爲結尾的最長非遞減子序列長度,每次轉移遍歷一整排數字,如果都滿足再進行轉移。

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 1000;
ll g[N][N];
int f[N]; //以i爲結尾的最長長度
 
int main()
{
#ifdef LOCAL
    freopen("C:/input.txt", "r", stdin);
#endif
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
    	for (int j = 1; j <= m; ++j)
        	scanf("%lld", &g[i][j]);
    int ans = 0;
    for (int i = 1; i <= n; ++i)
    {
        f[i] = 1;
        for (int j = 1; j < i; ++j)
        {
            int flag = 1;
            for (int k = 1; k <= m; ++k)
            if (g[j][k] <= g[i][k])
            {
                flag = 0;
                break;
            }
            if (flag)
                f[i] = max(f[i], f[j] + 1);
        }
        ans = max(ans, f[i]);
    }
    cout << n - ans << endl;
 
    return 0;
}

C 大小接近的點對 <樹狀數組>

使用樹狀數組查詢某個範圍內的數值的數量,因爲數值比較大需要先進行離散化處理。
使用DFS遍歷整棵樹,當到達某個節點時首先查詢區間[a[i]-m, a[i]+m]範圍內的數字數量記爲last,表示還沒到當前子樹時已有的數量。
將當前節點值加進梳妝數組,因爲自身到自身也算。進行遞歸,回溯後再次查詢區間[a[i]-m, a[i]+m]記爲now,表示增加了自身子樹之後的數量。
最後每個點的答案f[x]加上每個兒子的f[y],再加上now-last表示子樹節點能夠和當前節x點形成接近點對,即爲每個點的答案。

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e5 + 10;
int n, m;
int a[N];
vector<int> e[N];
int c[N];
ll f[N];
 
inline int lowbit(int x)
{
    return x & -x;
}
void Add(int x, int v)
{
    while (x < N)
        c[x] += v, x += lowbit(x);
}
int Ask(int x)
{
    if (x >= N)
        return 0;
    int res = 0;
    while (x)
        res += c[x], x -= lowbit(x);
    return res;
}
int RangeAsk(int l, int r)
{
    return Ask(r) - Ask(l - 1);
}
vector<int> dz;
int Diz(int x)
{
    return lower_bound(dz.begin(), dz.end(), x) - dz.begin();
}
void DFS(int x)
{
    int l = Diz(a[x] - m), r = Diz(a[x] + m);
    if (r >= dz.size() || dz[r] > a[x] + m) //離散化後的數值不一定出現需要特判
        --r;
    ll last = RangeAsk(l, r); //非子樹的相近點數量
    Add(Diz(a[x]), 1);
    for (int y : e[x])
        DFS(y), f[x] += f[y];
    ll now = RangeAsk(l, r); //原有+子樹的
    f[x] += now - last; //相減後即爲子樹的
}
int main()
{
#ifdef LOCAL
    freopen("C:/input.txt", "r", stdin);
#endif
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        scanf("%d", &a[i]), dz.push_back(a[i]);
    dz.push_back(-INF); //離散化 編號從1開始
    sort(dz.begin(), dz.end());
    dz.erase(unique(dz.begin(), dz.end()), dz.end());
    for (int i = 2; i <= n; ++i)
    {
        int x;
        scanf("%d", &x);
        e[x].push_back(i);
    }
    DFS(1);
    for (int i = 1; i <= n; ++i)
        printf("%lld\n", f[i]);
 
    return 0;
}

F 咕咕的計數題 II <數論>

將l除以a得到第一個可能再答案範圍內的除數L,r除以a得到最後一個可能再答案範圍內的除數R。
分爲3部分,1除數在[L, R]範圍,第一個和最後一個的位置可能只有一部分和lr相交需要特判。
2除了第一個則後面的每個除數對應除數個滿足條件的數字,如5 7 19,第一個爲[10, 11]長度爲10/5,第二個[15,17]長度爲15/5。這一部分進行等差數列求和即可。但是這種情況在除數大於等於a時會發生相交。
3所以最後一部分如果計算範圍大於等於a*a則後面的所有數字都會覆蓋。

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
ll a, l, r;
 
int main()
{
#ifdef LOCAL
    freopen("C:/input.txt", "r", stdin);
#endif
    int T;
    cin >> T;
    while (T--)
    {
        scanf("%lld%lld%lld", &a, &l, &r);
        ll L = l / a, R = r / a; //bei
        ll res = 0;
        if (L < a)
            res += max(0LL, min(a * L + L - 1, r) - max(a * L, l) + 1);
        ll L1 = L + 1, R1 = min(R - 1, a - 1);
        if (L1 <= R1)
            res += (L1 + R1) * (R1 - L1 + 1) / 2;
        if (a <= r / a)
            res += (r - max(l, a * a) + 1);
        else if(R > L)
            res += max(0LL, min(a * R + R - 1, r) - R * a + 1);
        printf("%lld\n", res);
    }
 
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章