18.8.29 考试总结

这道题就是分层图最短路啊啊啊啊啊啊啊 可惜我之前没写过 然后就很zz的没搞出来

dis[ i ][ j ]表示到了第 i 个点经过j个景点的最小距离

就是开一个超级源汇点 然后S向每个小起点连一条长度为排队时间的边 每个终点向T也连排队长度的边

然后就很愉快的SFPA了 最后判断答案就是在T处从大到小枚举景点数 如果发现被更新过就可以输出了 耶

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 5 * 1e3 + 5;
int V,m,n,e,l,sink,src,tot,head[N],nex[2 * N],tov[2 * N],val[2 * N];
ll dis[N][N];
bool vis[N][N];
struct sta {
    
    int u,num;
    sta(int u = 0,int num = 0): u(u),num(num) { }
};
queue<sta>Q;

void add(int u,int v,int w) {
    
    tot ++;
    nex[tot] = head[u];
    tov[tot] = v;
    val[tot] = w;
    head[u] = tot;
}

void init( ) {
    
    src = 0,sink = V + 1;
    for(int i = 1;i <= m;i ++) {
        int pos,pp;
        scanf("%d%d",& pos,& pp);
        add(src,pos,pp);
    }
    for(int i = 1;i <= n;i ++) {
        int pos,pp;
        scanf("%d%d",& pos,& pp);
        add(pos,sink,pp);
    }
    for(int i = 1;i <= e;i ++) {
        int u,v,w;
        scanf("%d%d%d",& u,& v,& w);
        add(u,v,w);
    }
}

void SPFA( ) {
    
    memset(dis,0x3f3f3f,sizeof(dis));
    ll cmp = dis[0][0];
    memset(vis,0,sizeof(vis));
    vis[src][0] = true;
    dis[src][0] = 0;
    Q.push(sta(src,0));
    while(! Q.empty( )) {
        sta a = Q.front( ); Q.pop( );
        vis[a.u][a.num] = false;
        for(int i = head[a.u];i;i = nex[i]) {
            int v = tov[i];
            if(dis[v][a.num + 1] > dis[a.u][a.num] + val[i] && dis[a.u][a.num] + val[i] <= l) {
                dis[v][a.num + 1] = dis[a.u][a.num] + val[i];
                if(! vis[v][a.num + 1]) {
                    vis[v][a.num + 1] = true;
                    Q.push(sta(v,a.num + 1));
                }
            }
        }
    }
    bool tag = true;
    for(int i = V + 1;i >= 1;i --) 
    if(dis[sink][i] != cmp) {
        printf("%d",i - 1);
        tag = false;
        break;
    }
    if(tag)  printf("0");
}

int main( ) {
    
    freopen("park.in","r",stdin);
    freopen("park.out","w",stdout);
    scanf("%d%d%d%d%d",& V,& m,& n,& e,& l);
    init( );
    SPFA( );
}

阿这道题是一道数学公式的题

可以预先用滑动窗口处理处每个点$i$向右第一个合法的点 记作$r[i]$ 因为从这以后每个点都必定合法

然后对于某个询问$[L,R]$  因为所有的$r[i]$都是单调不降的 所以可以二分求出在该区间内最后一个合法的位置

它的答案就是$\sum_{i = L}^{pos}(R - i + r[i] - i)\cdot (R - r[i] + 1) \cdot \frac{1}{2}$

然后化简得

$ (pos - L + 1) * (R \cdot R + R )\cdot \frac{1}{2} +  \sum_{i = L}^{pos}(r[i]  - r[i] \cdot r[i] + 2i\cdot r[i]+(R +1)\sum_{i = L}^{pos}i$

然后发现前面一坨和后面一坨都是可以直接算的 然后剩下的维护一个关于中间一坨的前缀和就可以了。

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 1e5 + 5;
int n,m,q,a[N],las[N];
ll sum[N],r[N];

void init( ) {
    for(int i = 1;i <= n;i ++) scanf("%d",& a[i]);
    int head = 1,tail = 1,tot = 0;
    for(head = 1;head <= n;head ++) {
        while(1) {
            if(tot >= m && tail - head > 1) {
                r[head] = tail - 1;
                break;
            }
            if(tail > n) {
                r[head] = N;
                break;
            }
            if(! las[a[tail]] && tail <= n)
                tot ++;
            las[a[tail]] = tail;
            tail ++;
        }
        if(las[a[head]] == head) {
            tot --;
            las[a[head]] = 0;
        }
    }
    for(int i = 1;i <= n;i ++) {
        sum[i] = sum[i - 1] + (r[i] - r[i]*r[i] + 2ll*i*r[i]);
    }
}

ll find_pos(ll L,ll R) {
    
    ll ans = 0;
    ll rr = R;
    while(L <= rr) {
        ll mid = (L + rr) >> 1;
        if(r[mid] <= R) ans = mid,L = mid + 1;
        else rr = mid - 1;
    }
    return ans;
}

void solve( ) {
    
    while(q --) {
        
        ll L,R;
        scanf("%I64d%I64d",& L,& R);
        ll pos = find_pos(L,R);
        if(pos == 0) {
            printf("0\n");
            continue;
        }
        ll R1 = (pos - L + 1) * (R * R + R) ;
        ll R2 = sum[pos] - sum[L - 1];
        ll R3 = (R + 1) * (L + pos) * (pos - L + 1) ;
        printf("%I64d\n",(R1 + R2 - R3)/2);
    }
}

int main( ) {
    
    freopen("plan.in","r",stdin);
    freopen("plan.out","w",stdout);
    scanf("%d%d%d",& n,& m,& q);
    init( );
    solve( );
      
}

这道题我是讲不清楚了 我下来改题 看代码的时间花费特别多 只能说blutrex大哥代码可读性太差了!!!!

而且还难得一批 最后几个点还T了 人生第一次被卡常数 呕

代码(是被卡常的代码 zc大佬和我写的一样结果他就过了!!! 是因为我这份代码太丑了??) 

#include <bits/stdc++.h>
using namespace std;

typedef long long ull;
const int N = 1e3;
const ull mod = 2000000011;
int n,m;
ull q[N],p[N],f[1 << (9 << 1)][65],a3[5];
struct dat {
    ull a,b;
    dat(ull a = 0,ull b = 0): a(a),b(b) { } 
}e[N];

ull fast_pow(ull a,ull b) {
    
    ull ans = 1;
    for(;b;b >>= 1,a = a * a % mod)
       if(b & 1) ans = ans * a % mod;
    return ans;
}

ull inverse(ull a) {
    
    return fast_pow(a,mod - 2);
}

int main( ) {
    freopen("card.in", "r", stdin);
    freopen("card.out", "w", stdout);
    
    scanf("%d%d",& n,& m);
    ull s = inverse(1ll * 100 * n);
    p[n] = 1;
    a3[0] = 0,a3[1] = inverse(3),a3[2] = 2 * inverse(3) % mod,a3[3] = 1;
    for(int i = 0;i < n;i ++)
       scanf("%I64d",& p[i]),p[i] = p[i] * s % mod,p[n] = (p[n] - p[i] + mod) % mod;
    q[n] = 1;
    for(int i = 0;i < n;i ++)
       scanf("%I64d",& q[i]),q[i] = q[i] * s % mod,q[n] = (q[n] - q[i] + mod) % mod;
    for(int s = (1 << (n << 1)) - 2;s >= 0;s --) {
        e[m] = dat(1,0);
        ull v = 0;
        ull ss = q[n];
        for(int i = 0;i < n;i ++) {
            ss = (ss + q[i] * a3[(s >> (i << 1) & 3)]) % mod;
            if((s >> (i << 1) & 3) < 3)
            v = (v + q[i] * a3[((s >> (i << 1) & 3) ^ 3)] % mod * f[s + (1 << (i << 1))][m]) % mod;
        }
        e[m - 1] = dat(ss * e[m].a % mod,(ss * e[m].b % mod + v) % mod);
        for(int j = m - 2;j >= 0;j --) {
            ull ss = p[n]; v = 0;
            for(int i = 0;i < n;i ++) {
                ss = (ss + p[i] * a3[(s >> (i << 1) & 3)]) % mod;
                if((s >> (i << 1) & 3) < 3)
                v = (v + p[i] * a3[((s >> (i << 1) & 3) ^ 3)] % mod * f[s + (1 << (i << 1))][j + 1]) % mod;
            }
            e[j] = dat(ss * e[j + 1].a % mod,(ss * e[j + 1].b % mod + v) % mod);
        }
        f[s][0] = (e[0].a + e[0].b) % mod * inverse(1 - e[0].a + mod) % mod;
        f[s][m] = (f[s][0] + 1) % mod;
        for(int k = 1;k < m;k ++) f[s][k] = (e[k].a * f[s][m] + e[k].b ) % mod;
    }    
    printf("%I64d",f[0][m] % mod);
}

猜你喜欢

转载自www.cnblogs.com/Rubenisveryhandsome/p/9557286.html