2020ICPC 小米 网络选拔赛第二场

比赛链接


D - Determinant(行列式化简)

题目大意

给出行列式 M i j = { a i b j i ≠ j a i b j + x i = j M_{ij} = \left\{\begin{array}{rcl} a_ib_j & i \neq j \\ a_ib_j+x & i = j \end{array}\right. Mij={aibjaibj+xi=ji=j,求行列式的值。

解题思路

这道题考了一个不太常见的点——行列式化简计算。比赛时队友推出来了我没推出来,课下好好总结了一下,见这里

本题可以通过升阶法和数学归纳法(对低阶行列式初等变换找到规律)求解,由于过程复杂就不再贴出。

最后的答案是 a n s = x n + x n − 1 ∑ i = 1 n a i b i ans = x^n + x^{n-1}\sum_{i=1}^na_ib_i ans=xn+xn1i=1naibi

//
// Created by Happig on 2020/10/31
//
#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>

using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define ENDL "\n"
#define lowbit(x) (x&(-x))
#define mkp(x, y) make_pair(x,y)
#define mem(a, x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 2e5 + 10;

ll a[maxn], b[maxn];

ll qkp(ll x, ll n, ll p) {
    ll ans = 1;
    x %= p;
    while (n) {
        if (n & 1) ans = ans * x % p;
        x = x * x % p;
        n >>= 1;
    }
    return ans;
}

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n, x;
    while (cin >> n >> x) {
        for (int i = 1; i <= n; i++) cin >> a[i];
        for (int i = 1; i <= n; i++) cin >> b[i];
        ll xx = qkp(x, n - 1, Mod), ans = 0;
        for (int i = 1; i <= n; i++) {
            ans = (ans + xx * a[i] % Mod * b[i] % Mod) % Mod;
        }
        ans = (ans + xx * x % Mod) % Mod;
        cout << ans << endl;
    }
    return 0;
}
G - Shift and Reverse

题目大意

给出一个序列 a 1 a 2 . . . a n a_1a_2...a_n a1a2...an,现在可以任意选定一个元素 a i a_i ai,然后将序列翻转为 a i . . . a 2 a 1 a n a n − 1 . . . a i + 1 a_i...a_2a_1a_{n}a_{n-1}...a_{i+1} ai...a2a1anan1...ai+1,问整个序列能翻转为多少种不同的序列。

解题思路

这题赛时通过的并不多,我们也没开这道题。实际上,将序列想象成一个环,实际上就是可以选择一个数断开,然后逆时针可以得到一个序列,这样虽然看起来得到了 n n n种序列,但是我们对每个断开位置的新序列的位置继续断开,发现得到是一个原序列断开位置顺时针的序列,这样一共只有 2 n 2n 2n种序列,那么化环为线,使用哈希判断即可。(蒟蒻喜欢自然溢出哈希)


//
// Created by Happig on 2020/11/8
//
#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>

using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define ENDL "\n"
#define lowbit(x) (x&(-x))
#define mkp(x, y) make_pair(x,y)
#define mem(a, x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 2e5 + 10;

ull base = 131;

int a[maxn], b[maxn];
ull rad[maxn];
unordered_map<ull, int> mp;

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n;
    rad[0] = 1;
    for (int i = 1; i <= maxn >> 1; i++) {
        rad[i] = rad[i - 1] * base;
    }
    while (cin >> n) {
        mp.clear();
        ull res1 = 0, res2 = 0;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            a[n + i] = a[i];
            res1 = a[i] + res1 * base;
        }
        for (int i = n, j = 1; i >= 1; i--, j++) {
            b[j] = a[i], b[n + j] = b[j];
            res2 = b[j] + res2 * base;
        }
        int ans = 0;
        if (res1 == res2) ans = 1;
        else ans = 2;
        mp[res1] = 1, mp[res2] = 1;
        for (int i = n + 1; i <= 2 * n; i++) {
            res1 = (res1 - rad[n - 1] * a[i - n]) * base + a[i];
            if (!mp.count(res1)) {
                mp[res1] = 1, ans++;
            }
        }
        for (int i = n + 1; i <= 2 * n; i++) {
            res2 = (res2 - rad[n - 1] * b[i - n]) * base + b[i];
            if (!mp.count(res2)) {
                mp[res2] = 1, ans++;
            }
        }
        cout << ans << endl;
    }
    return 0;
}
H - Knapsack(背包+四边形不等式优化)

题目大意

01背包裸题,但是物品个数和背包容量都达到了 2 e 5 2e^5 2e5,物品的价值达到了 1 e 9 1e^9 1e9,而物品的重量却只有 100 100 100

解题思路

假算法

网上流传的一个解法:先按重量价值的比值贪心选直到 m m m较小,然后进行DP,这样的思路实际上是错误的。在知乎上叉姐已经说了,因为数据不够强导致了这个假算法。

我的思路

我想的是,因为重量较小,对于每种重量,我么一定优先选择价值最大的,因此我们按重量分类,问题变成了多重背包,对于每种物品选择若干个,其最优价值显然是价值降序后的前缀和。这样只需使用单调队列解决完全背包问题,时间复杂度 O ( 100 ∗ m ) O(100*m) O(100m),很遗憾这种解法是 T L E TLE TLE的,不知道是代码问题还是解法问题,希望大佬告知!

//
// Created by Happig on 2020/10/31
//
#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>

using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define ENDL "\n"
#define lowbit(x) (x&(-x))
#define mkp(x, y) make_pair(x,y)
#define mem(a, x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 2e5 + 10;

vector<int> v[105];  //价值
ll sum[105][maxn];

ll f[maxn], g[maxn], q[maxn];


int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    //ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n, m;
    while (scanf("%d%d", &n, &m) != EOF) {
        memset(f, 0, sizeof f);
        for (int i = 1; i <= 100; i++) {
            sum[i][0] = 0;
            v[i].clear();
        }
        for (int i = 1, x, y; i <= n; i++) {
            scanf("%d%d", &x, &y);
            v[x].push_back(y);
        }
        for (int i = 1; i <= 100; i++) sort(v[i].begin(), v[i].end(), greater<int>());
        for (int i = 1; i <= 100; i++) {
            for (int j = 0; j < v[i].size(); j++) {
                sum[i][j + 1] = sum[i][j] + v[i][j];
            }
        }
        for (int i = 1, s; i <= 100; i++) {
            if (v[i].size() == 0) continue;
            //物品的体积为i,取k个的价值为sum[i][k],个数为v[i].size()
            s = v[i].size();
            memcpy(g, f, sizeof f);
            for (int j = 0; j < i; j++) {  //枚举每个V%c(0<=V<=m),每一类余数相同的才会相互转移
                int l = 0, r = -1;
                for (int k = j; k <= m; k += i) {  //枚举j+k*c<=m的所有容量
                    f[k] = g[k];
                    if (l <= r && k - s * i > q[l]) l++;  //左边最大值小于当前最小剩余容量则删去
                    if (l <= r) f[k] = max(f[k], g[q[l]] + sum[i][(k - q[l]) / i]);    //更新答案
                    while (l <= r && g[q[r]] - sum[i][(q[r] - j) / i] <= g[k] - sum[i][(k - j) / i])
                        r--;  //右边小于等于当前元素的剔除
                    q[++r] = k;  //插入当前容量
                }
            }
        }
        printf("%lld\n", f[m]);
    }
    return 0;
}

正确思路

因为我对DP的各种优化还没有深入学习,这篇博客讲解了四边形不等式如何优化这道题,先留坑吧!

I - Subsequence Pair(类LCS的DP)

题目大意

给出两个字符串 s , t s,t s,t,分别从中选择一个子序列,使得前者的子序列 s 0 s_0 s0小于等于后者的子序列 t 0 t_0 t0(子序列的比较就是字符串大小比较)

解题思路

如果能想到LCS,这题已经成功一半了。设 d [ i ] [ j ] d[i][j] d[i][j]为第一个串位置为 i i i,第二个串位置为 j j j的LCS的长度,第一个串的长度为 n n n,第二个串的长度为 m m m。主要是分为以下两种情况考虑:

  • s [ i ] = t [ j ] s[i]=t[j] s[i]=t[j],这两个字符一定会选择。因为我们不知道后面位置的大小,但是可以肯定对于 t 0 t_0 t0来说加上末尾所有字符仍会满足 s 0 ≤ t 0 s_0 \leq t_0 s0t0,那么此时 a n s = m a x ( a n s , 2 ∗ d [ i − 1 ] [ j − 1 ] + 1 + m − j + 1 ) ans = max(ans,2*d[i-1][j-1]+1+m-j+1) ans=max(ans,2d[i1][j1]+1+mj+1)
  • s [ i ] < t [ j ] s[i] < t[j] s[i]<t[j],这两个字符也一定会选择。现在可以知道子序列的大小了因此两个串后面的都可以直接接上去。但是前面呢,显然是取前部分的LCS,即: a n s = m a x ( a n s , 2 ∗ d [ i − 1 ] [ j − 1 ] + n − i + 1 + m − j + 1 ) ans = max(ans,2*d[i-1][j-1]+n-i+1+m-j+1) ans=max(ans,2d[i1][j1]+ni+1+mj+1)

//
// Created by Happig on 2020/11/6
//
#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>

using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define ENDL "\n"
#define lowbit(x) (x&(-x))
#define mkp(x, y) make_pair(x,y)
#define mem(a, x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 2020;

char s1[2020], s2[2020];
int d[2020][2020];

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    //ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    while (scanf("%s", s1 + 1) != EOF) {
        scanf("%s", s2 + 1);
        int n = strlen(s1 + 1), m = strlen(s2 + 1);
        memset(d, 0, sizeof d);
        int ans = m;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                if (s1[i] == s2[j]) {
                    d[i][j] = d[i - 1][j - 1] + 1;
                    ans = max(ans, 2 * d[i][j] + m - j);
                } else {
                    d[i][j] = max(d[i - 1][j], d[i][j - 1]);
                    if (s1[i] < s2[j]) {
                        ans = max(ans, 2 * d[i - 1][j - 1] + n - i + 1 + m - j + 1);
                    }
                }
            }
        }
        cout << ans << ENDL;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值