2015-9-13 NOIP模拟赛 by hzwer

时间:2021-12-23 09:33:39

好老的题了,但是还是很有做头的。

总结

  1. 不吸氧看来确实是没法用stl的啊(set常数太大了,开O2也没过)
  2. SPFA没认真学,觉得有堆优化Dijkstra就天下无敌了,今天负边权教我做人
  3. 于是苦逼的只有180分
  4. 第一题我看错了,想了10分钟,本来打算打暴力50分,然后又看对了。 /(ㄒoㄒ)/~~
  5. 后来发现其实打Bellman-Ford可以拿下40分,但是我没有认真想(何况Floyd算法也可以)

小奇挖矿

题目分析

我们发现这道题每个操作只对其后面又影响,然后我们想到DP要求的无后效性,于是我们直接倒着DP一下就好了。

(最重要的是这道题有取东西的顺序)

代码

#include <cstdio>
#include <iostream>
using namespace std;

#define forn(i) for(int i=1;i<=n;++i)

const int maxn = 1e5 + 10;
int opt[maxn], a[maxn];

int main() {
    freopen("explo.in","r",stdin);
    freopen("explo.out","w",stdout);
    double n, k, c, w;
    cin >> n >> k >> c >> w;
    forn(i) cin >> opt[i] >> a[i];
    double p_1 = 1-0.01*k;
    double p_2 = 1+0.01*c;
    double ans = 0;
    for (int i = n; i; -- i)
        if (opt[i] & 1) ans = max(ans, ans * p_1 + a[i]);
        else            ans = max(ans, ans * p_2 - a[i]);
    printf("%.2lf", ans*w);
    return 0;
}

小奇的数列

题目分析

emmm,这道题暴力都可以70分。我怕不吸氧set过不了,于是写了个70的部分分,然后写了个stl版正解

70pts: 首先根据抽屉原理,如果询问长度 \(\ge q\) 那就必有前缀和模\(q\)相等,或者这个数本身被\(q\)整除,然后因为\(q \le 200\)直接\(n ^ 2\)暴力

100pts: 考虑已经定下来一端,我们只需要寻找另外一段,是的他们的差值最小就行了,于是自然的写出二分。维护的话,考虑平衡树,或者set都可以。

代码

set

#include <cstdio>
#include <iostream>
#include <set>
using namespace std;
typedef long long qword;
#define pb push_back

#define forn(i) for(int i=1;i<=n;++i)
#define rep(i,s,t) for(int i=(int)(s);i<=(int)(t);++i)

int n, m;
const int maxn = 5e5 + 10;
int a[maxn];
qword sum[maxn];

inline void Init() {
    cin >> n >> m;
    forn(i) cin >> a[i];
    forn(i) sum[i] = sum[i - 1] + a[i];
}

qword mod(qword x, int p) {
    x %= p;
    while (x < 0) x += p;
    return x % p;
}


inline void work() {
    int L, R, Q;
    qword ans;
    while (m--) {
        cin >> L >> R >> Q;
        ans = Q;
        if (R - L >= Q) {cout << 0 << endl; continue;}
        rep(i,L,R) rep(j,i,R)
            ans = min(ans, mod(sum[j] - sum[i - 1],Q));
        cout << ans << endl;
    }
}

inline void solve() {
    int L, R, Q;
    qword ans;
    while (m--) {
        cin >> L >> R >> Q;
        ans = Q;
        if (R - L + 1 >= Q) {cout << 0 << endl; continue;}
        set<qword> s;
        s.insert(0);
        rep(i,L,R) {
            qword w = mod(sum[i] - sum[L - 1], Q);
            set<qword>::iterator it = s.upper_bound(w);
            if (it != s.begin()) --it;
            ans = min(ans, mod(w - *it, Q));
            s.insert(w);
        }
        cout << ans << endl;
    }
}

#define OK

int main() {
#ifdef OK
    freopen("seq.in","r",stdin);
    freopen("seq.out","w",stdout);
    ios::sync_with_stdio(0);
#endif // OK
    Init();
    if(n <= 100000 && m<=10000) work();
    else solve();
}

小奇会地球

题意

给一个有负边的n个点的图,如果能给全图边权同时加上(或减去)一个值t,问图中1到n的最短路距离非负时的最小距离。

题目解析

首先跑有负边图的最短路只能SPFA。

由于t和答案同增同减,具有单调性。我们可以二分t来取符合条件的最小距离。

如果通过负环不能到达n号点,这个负环实际上可以忽略。

所以开头建反边,从n跑dfs看能到达哪些点,只对这些点跑最短路即可。

复杂度\(O(Tn^2logt)\)

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>

#define rep(i,s,t) for(int i = (int)(s); i <= (int)(t); ++ i)
#define seta(a,x) memset (a, x, sizeof (a))
#define tral(i) for(int i=h[u];i+1;i=e[i].nxt)
typedef long long qword;

using namespace std;

const int mod=1e9+7,N=305;

struct Edge{int to,nxt,w;}e[N*N];
struct dat{int u,v,w;}a[N*N];
int h[N],n,m,cnt,dis[N],mn,mx,num[N];
bool vis[N],viss[N];

queue<int> Q;

inline void add( int u, int v, int w){e[++cnt]=(Edge){v,h[u],w};h[u]=cnt;}

inline int SPFA(int x) {
    rep(i,1,n) dis[i] = 1e9, num[i] = 0, vis[i] = 0;
    while(!Q.empty()) Q.pop();
    Q.push(1); vis[1] = 1; dis[1] = 0;
    while(!Q.empty()) {
        int u=Q.front();Q.pop();
        tral(i) {
            int v = e[i].to;
            if(!viss[v]) continue;
            if(dis[v] > dis[u]+e[i].w+x) {
                if((++num[v]) > n) return -1e9;
                dis[v] = dis[u]+e[i].w+x;
                if(!vis[v]) vis[v] = 1, Q.push(v);
            }
        }
        vis[u] = 0;
    }
  return dis[n];
}
inline void dfs(int u) {
    viss[u] = 1;
    tral(i) {
        int v = e[i].to;
        if(viss[v]) continue;
        dfs(v);
    }
}
int main() {
    int T;
    cin >> T;
    while(T--) {
        seta(h, -1);
        cnt = 0; mn = 1e9; mx = -1e9;
        cin >> n >> m;
        rep(i,1,m) {
            cin >> a[i].u >> a[i].v >>a[i].w;
            add(a[i].v, a[i].u, a[i].w);
            mn = min(mn, a[i].w); mx = max(mx, a[i].w);
        }
        dfs(n);
        seta(h, -1); cnt = 0;
        rep(i,1,m) add(a[i].u, a[i].v, a[i].w);
        if(SPFA(-mn + 100) == 1e9) {cout << "-1" << endl;continue;}
        int l = -mx, r = -mn, ans = 0;
        while(l <= r) {
            int mid = l+r >> 1, check = SPFA(mid);
            if(check >= 0) ans = check,r = mid-1;
            else l = mid+1;
        }
      cout << ans << endl;
    }
    return 0;
}