Monopoly 解题报告

网络赛的时候没有搞出来,前几天做Atcoder的时候做到了一场STL场的比赛,就回来一起把这个题也补了。
题解:
首先肯定的是,如果当前的 x x x合法,一定可以在序列中找到一个 a [ i ] a[i] a[i],满足: x ≡ a [ i ] m o d d x \equiv a[i] \enspace mod \enspace d xa[i]modd,其中 d d d a [ i ] a[i] a[i]的总和。
并且我们发现,当 d < 0 d < 0 d<0时,我们可以把 a [ i ] a[i] a[i] x x x全部取反,这样再讨论其实和正数的情况是一致的。
所以我们的思路综述:

  • d = 0 d = 0 d=0:直接在序列中查找有没有 x = a [ i ] x = a[i] x=a[i]存在。
  • d > 0 d > 0 d>0:找到满足 x ≡ a [ i ] m o d d x \equiv a[i] \enspace mod \enspace d xa[i]modd a [ i ] a[i] a[i],如果存在多个,则找到距离 x x x最近且小于它的数。
  • d < 0 d < 0 d<0:把 x x x a [ i ] a[i] a[i]全部取反,这样就又回到了第二种情况。

如何设计算法解决 d > 0 d > 0 d>0的问题?
mapvectorpair就行
我们把和 x x x同余的每一种情况建立一个map<int, vector<?> >,同时,我们的vector里希望存放的信息有: a [ i ] a[i] a[i] i i i,那就可以建立一个pair,于是最后我们就可以建立出map<int, vector<pair<int, int> > >
然后对每一个vector进行排序,对于输入的每一个 x x x,找到对应的 x m o d d x \enspace mod \enspace d xmodd,在这一维度的vector中查找满足条件的 a [ i ] , i a[i],i a[i],i
最后的答案显然是 i + ( x − a [ i ] ) d ∗ n i + \frac{(x - a[i])}{d} * n i+d(xa[i])n

//This code is written by Kyrie Qi
//QQ : 601383880
//Email : cuc_qzl@cuc.edu.cn

#include <bits/stdc++.h>

#define fi first
#define se second
#define ill __int128
#define ll long long
#define pb push_back
#define mkp make_pair
#define lson(x) x << 1
#define lowbit(x) (x & -x)
#define PII pair <ll, ll>
#define rson(x) (x << 1 | 1)
#define ull unsigned long long
#define me(a,b) memset (a, b, sizeof(a))
#define ios std :: ios :: sync_with_stdio(false)
#define debug(x) cout << #x << " = " << x << endl

const double Exp = 1e-9;
const int INF = 0x3f3f3f3f;
const int inf = -0x3f3f3f3f;
const int mode = 1000000007;
const double pi = acos(-1.0);

using namespace std;

const int maxn = 1e5 + 10;
int t, n, m;
ll a[maxn], x, sum[maxn];
map<ll, vector<PII>> mp;
map<ll, int> ma;

int main()
{
    scanf ("%d", &t);
    while (t -- ) {
        mp.clear();
        ma.clear();
        me (sum, 0ll);
        scanf ("%d%d", &n, &m);
        for (int i = 1;i <= n;i ++) {
            scanf ("%lld", &a[i]);
            sum[i] = a[i] + sum[i - 1];
        }
        if (sum[n] == 0ll) {
            for (int i = 1;i <= n;i ++) if (!ma[sum[i]]) ma[sum[i]] = i;
            for (int i = 1;i <= m;i ++) {
                scanf ("%lld", &x);
                if (x == 0) printf ("0\n");
                else if (ma[x]) printf ("%d\n", ma[x]);
                else printf ("-1\n");
            }
        }
        else {
            bool f = 0;
            if (sum[n] < 0) {
                f = 1;
                for (int i = 1;i <= n;i ++) sum[i] = -sum[i];
            }
            for (int i = 1;i <= n;i ++) {
                int d = (sum[i] % sum[n] + sum[n]) % sum[n];
                mp[d].push_back(mkp(-sum[i], i)); //注意这里很贼的方法,省去了自定义排序。
            }
            for (auto it = mp.begin();it != mp.end();it ++) sort (it->second.begin(), it->second.end());  //sum从大到小,i从小到大。
            for (int i = 1;i <= m;i ++) { 
                scanf ("%lld", &x);
                if (f) x = -x;
                if (x == 0) {
                    printf ("0\n");
                    continue;
                }
                int d = (x % sum[n] + sum[n]) % sum[n];
                if (mp[d].empty()) {
                    printf ("-1\n");
                    continue;
                }
                else {
                    PII tmp = {-x, 0};
                    int p = lower_bound(mp[d].begin(), mp[d].end(), tmp) - mp[d].begin();
                    if (p == mp[d].size()) printf ("-1\n");
                    else {
                        ll ans = mp[d][p].second;
                        ans += (x + mp[d][p].first) / sum[n] * n;
                        printf ("%lld\n", ans);
                    }
                }
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CUCKyrie

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值