bzoj 2118: 墨墨的等式

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2118
思路:这是一道经典问题,所以虽然不是自己想出来的还是在博客里写一下,练习了堆优化dijistra,观察,可以发现变量个数和系数范围还是比较小的,然而解的个数和规模确大到无法处理,这类问题,,,是有通法的,,,考虑,如何将解归类,也就是根据特征划分为若干集合,按集合整体快速计算,集合个数要小,单个集合计算要快O(1)或O(logn)都是可以的,也就是将解空间单射到另一个空间,往往题目中有一些数据达到了10^7以上,不看他,我们就看那些10^6以下的较小的数据,以本题为例子:发现系数很小,那么我们进行归类,每一个解都能唯一的表示为x = p*q + r的形式,也就是说形成单射,这提示我们根据余数分类,考虑,我们如果选取某个模数为p,余数为r,那么如果
x可以被如此表示,那么x + j*p都是可以的,然而我们要找最小的满足条件的x,怎么办呢,,而且是要找很多很多x,,数论走不通?图论建模,最短路模型非常简单,直接搞好了,,,
同类题目:dzy loves math 2
(未完待续)
代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#define N 12
#define M 500000
#define inf 1e9
using namespace std;
typedef long long LL;
struct edge{ int nxt,point,v; LL w;};
struct data{ LL value; int id;};
edge e[((N*M)<<1)+5];
data heap[M+5];
LL a[N+5],dis[M + 5],cnt,n,Bmin,Bmax,mini,minist;
void addedge(int u1,int v1,LL w1){ 
    e[++cnt].nxt = e[u1].point; e[u1].point = cnt; e[cnt].v = v1; e[cnt].w = w1;}
//void insert(int u1,int v1){ addedge(u1,v1); addedge(v1,u1);}
inline LL getnum(){
    char c; LL num;
    while (!isdigit(c = getchar()));
    num = c - '0';
    while (isdigit(c = getchar())) num = 10 * num + c - '0';
    return num;
}

void init(){
    n = getnum(); Bmin = getnum(); Bmax = getnum(); cnt = 0;
    for (int i = 1;i <= n; ++i) a[i] = getnum();
    sort(a + 1,a + n + 1);
    mini = a[1];
}

void make_it(){
    for (int i = 0;i < mini; ++i)
      for (int j = 1;j <= n; ++j)
        addedge(i,(i + a[j])%mini,a[j]);
}

inline void push(data x){
    heap[++cnt] = x;
    int j = cnt;
    while (j>1&&heap[j].value < heap[(j>>1)].value) swap(heap[j],heap[j>>1]);
}

inline data pop(){
    data x = heap[1];
    heap[1] = heap[cnt--];
    int j = 1;
    while (1){
        minist = j;
        if ((j<<1)<=cnt) minist = j<<1;
        if ((j<<1|1)<=cnt&&heap[minist].value > heap[(j<<1|1)].value) minist = j<<1|1;
        if (minist == j) break;
        swap(heap[j],heap[minist]);
        j = minist; 
    }
    return x;
}

void heap_dijistra(){
    data tmp = (data) {0,0}; cnt = 0;
    memset(dis,0x7f,sizeof(dis));
    dis[0] = 0; push(tmp); 
    while (cnt){
         data x = pop(); 
         //cout<<x.id<<" "<<x.value<<endl;
         for (int p = e[x.id].point;p ; p = e[p].nxt)
           if (dis[e[p].v] > dis[x.id] + e[p].w){
                dis[e[p].v] = dis[x.id] + e[p].w;
                push((data){dis[e[p].v],e[p].v});
           }
    }
}

inline LL query(LL maxi){
    if (maxi == 0) return 1;
    if (maxi < 0) return 0;
    LL sum = 0; 
    for (int i = 0;i < mini; ++i)
      if (dis[i] <= maxi)
        sum += (maxi - dis[i])/mini + 1;
    return sum;
}

void DO_IT(){
    make_it();
    heap_dijistra();
    //cout<<query(10)<<endl;
    //cout<<query(4);
    cout<<query(Bmax) - query(Bmin - 1);
}

int main(){
    init();
    DO_IT();
    return 0;
}
总结:快速读入和数据该longlong就longlong,爆了很久int也没发现,,
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值