AtCoder Beginner Contest #164 (DE)

比赛链接

D
题意:
给你一个长度2e5的字符串,由数字组成,问你有多少个子串能被2019整除。

思路:
跟这道题一样:基本一样的题,ABC round 158的E题,这种求子串满足整除某个数p的问题,基本套路就是维护一个从结尾到首的数模p的值,然后扫完一遍,就从模下来每个可能的值随意取两个,因为这相同的两个模数的位置之间的那个数字串肯定是能整除p的,注意如果模p是2和5就要特别讨论一下,因为他们是10的因数,也就是158E题要考虑的。

#include <bits/stdc++.h>

#define eb emplace_back
#define mp make_pair
#define mt make_tuple
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
#define forn(i, n) for (int i = 0; i < (int)(n); ++i)
#define for1(i, n) for (int i = 1; i <= (int)(n); ++i)
#define ford(i, a, b) for (int i = (int)(a); i >= (int)b; --i)
#define fore(i, a, b) for (int i = (int)(a); i <= (int)(b); ++i)
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define ms(x, y) memset(x, y, sizeof(x))
#define SZ(x) ((int)(x).size())

using namespace std;

typedef pair<int, int> pii;
typedef vector<int> vi;
typedef vector<pii> vpi;
typedef vector<vi> vvi;
typedef long long i64;
typedef vector<i64> vi64;
typedef vector<vi64> vvi64;
typedef pair<i64, i64> pi64;
typedef double ld;

template<class T> bool uin(T &a, T b) { return a > b ? (a = b, true) : false; }
template<class T> bool uax(T &a, T b) { return a < b ? (a = b, true) : false; }

const int mod = 2019;
const int maxn = 200000+100;
i64 pref[maxn], cnt[maxn];
string s;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.precision(10);
    cout << fixed;
#ifdef LOCAL_DEFINE
    freopen("input.txt", "r", stdin);
#endif

    ms(cnt, 0);
    cin >> s;
    int len = SZ(s);
    int ten = 1, now = 0;
    for (int i = len-1; i >= 0; --i) {
        int num = int(s[i] - '0');
        now = (ten*num+now)%mod;
        pref[i] = now;
        ++cnt[now];
        ten = (ten*10)%mod;
    }
    i64 ans = 0;
    ++cnt[0];
    for (int i = 0; i < 2019; ++i) {
        if (cnt[i] >= 2) {
            ans += cnt[i]*(cnt[i]-1)/2;
        }
    }
    cout << ans << '\n';

#ifdef LOCAL_DEFINE
    cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
#endif
    return 0;
}

E
题意:
有n个点(<=50),编号为1到n,m条边(<=100), 你开局在1号点,有10^100的金币和s(0 <= s <=1e9)个银币,每个点都有两个属性c(1 <= c <= 1e9)和d(1 <= d <= 1e9),表示每d秒钟可以将你的1个金币换为c个银币,m条边 u, v, a, b,表示u和v之间有条双向边,消耗a(1 <= a <= 1e9)个银币和b(1 <= b <= 1e9)秒经过。现在问你从1号点分别到2 ~ n号点的最短耗时。

思路:
首先假设有50个点,每条边都要50个银币,而且c等于1,走完所有点需要49条边,那么最多需要的金币是49*50 = 2450,所以金币肯定是远远够用的, 我设最多需要3000就够,先把s和3000取个min,至于为什么,下面要开dp数组,s取值是可以到1e9的,会runtime error。
先搞了dp数组,dp[i][j]表示第i个点有j个银币的最小耗时,j我开了3000大小,比2450大就行。

现在就跑一边dijkstra就行,每个点有两个属性,当前在哪个点和目前的耗时,pop出来的每个点都要枚举一下如果在此处换金币的情况,用dp维护要不要更新,松弛操作里就是维护从当前过去的状态看看要不要更新,如果要更新的话就push进去。

现在来看时间复杂度:
最坏情况,假设每个dp[50][3000]每个情况都有,那么优先队列的时间复杂度就是50*3000*log(50*3000)大概是1e6,每次还要跑100条边,复杂度就是1e8,给了2s,应该能过,应为不可能每次都跑100条边的,1e8肯定跑不满。

我觉得自己讲的也听模糊的,看代码吧。

#include <bits/stdc++.h>

#define eb emplace_back
#define mp make_pair
#define mt make_tuple
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
#define forn(i, n) for (int i = 0; i < (int)(n); ++i)
#define for1(i, n) for (int i = 1; i <= (int)(n); ++i)
#define ford(i, a, b) for (int i = (int)(a); i >= (int)b; --i)
#define fore(i, a, b) for (int i = (int)(a); i <= (int)(b); ++i)
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define ms(x, y) memset(x, y, sizeof(x))
#define SZ(x) ((int)(x).size())

using namespace std;

typedef pair<int, int> pii;
typedef vector<int> vi;
typedef vector<pii> vpi;
typedef vector<vi> vvi;
typedef long long i64;
typedef vector<i64> vi64;
typedef vector<vi64> vvi64;
typedef pair<i64, i64> pi64;
typedef double ld;

template<class T> bool uin(T &a, T b) { return a > b ? (a = b, true) : false; }
template<class T> bool uax(T &a, T b) { return a < b ? (a = b, true) : false; }

const int maxn = 110; //点数
const i64 INF = 0x3f3f3f3f3f3f3f3fLL;
struct node {
    int id, w, money;  // w == distance
    node(){}
    node(int a, int b, int c) : id(a), w(b), money(c) {}
    friend bool operator < (node a, node b) {return a.w > b.w;}
};
vector<node> G[maxn];
i64 dp[55][3050], a[maxn], b[maxn], c[maxn], d[maxn];
i64 n, m, s;

void dij(int start, int n) {
    for (int i = 0; i <= 50; ++i) {
        for (int j = 0; j <= 3000; ++j) {
            dp[i][j] = INF;
        }
    }
    priority_queue<node> q;
    node cur;
    q.push(node(start, 0, s));
    dp[start][s] = 0;
    while (!q.empty()) {
        cur = q.top();
        q.pop();
        int u = cur.id;
        int temp = cur.money+c[u];
        temp = min(temp, 3000);
        if (dp[u][temp] > dp[u][cur.money]+d[u]) {
            dp[u][temp] = dp[u][cur.money]+d[u];
            q.push(node(u, dp[u][temp], temp));
        }
        for (node to : G[cur.id]) {
            if (cur.money >= to.money
                && dp[to.id][cur.money-to.money] > dp[cur.id][cur.money]+to.w) {
                dp[to.id][cur.money-to.money] = dp[cur.id][cur.money]+to.w;
                q.push(node(to.id, dp[to.id][cur.money-to.money], cur.money-to.money));
            }
        }
    }
}
void init(int n) {
    for (int i = 0; i <= n; ++i) G[i].clear();
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.precision(10);
    cout << fixed;
#ifdef LOCAL_DEFINE
    freopen("input.txt", "r", stdin);
#endif

    cin >> n >> m >> s;
    s = min(s, 3000LL);
    init(n);
    forn(i, m) {
        int u, v;
        cin >> u >> v >> a[i] >> b[i];
        G[u].eb(node(v, b[i], a[i]));
        G[v].eb(node(u, b[i], a[i]));
    }
    for1(i, n) cin >> c[i] >> d[i];
    dij(1, n);
    for (int i = 2; i <= n; ++i) {
        i64 ans = LLONG_MAX;
        for (int j = 0; j <= 3000; ++j) {
            ans = min(ans, dp[i][j]);
        }
        cout << ans << '\n';
    }

#ifdef LOCAL_DEFINE
    cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
#endif
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值