HDU多校 第五场 6227 (equation)

传送门

题意:

对于 ∑ 1 n ∣ a i x + b i ∣ = C \sum_1^n|a_ix+b_i| = C 1naix+bi=C, 求 x x x的所有可行解(分数形式表示)。

思路:
分区间讨论。

我们令 ∣ a i x + b i ∣ = 0 |a_ix+b_i| = 0 aix+bi=0,那么就可以得到 n n n个关于 a , b a,b a,b 的解 x i x_i xi,(对每个绝对值编号1~i)
按照 x i x_i xi 的大小(为了避免精度误差,采用交差相乘的方法对 x i x_i xi排序)排序。
然后根据高中知识 (绝对值分类讨论)

假设我们从数轴的最左端开始向右移动, 对于 x i x_i xi x i + 1 x_{i+1} xi+1这个区间,区间内的所有实数都会使得 1 1 1 i i i的绝对值内的方程恒正, i + 1 i+1 i+1 n n n的绝对值恒负。

因为我们要求的是可行解,那么对于所有绝对值有 ∑ 1 i a i ∗ x + ∑ 1 i b i = C \sum_1^ia_i*x+\sum_1^ib_i = C 1iaix+1ibi=C

因此我们只需要维护 a i a_i ai b i b_i bi 的前缀和,每次向右移动区间不断更新前缀和就可以了。
其他的就是一些细节。比如注意如果解是负数负号要在分子。注意去重。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
typedef long long ll;
ll gcd(ll a,ll b){
    return b==0?a:gcd(b,a%b);
}
struct node{
    int a,b;
    int id;
    bool operator<(const node A)const{
        return (-b)*A.a < a*(-A.b);
    }
}w[maxn];

struct Node{
    ll xa,xb;
    bool operator<(const Node A)const{
        return xa*A.xb < xb*A.xa;
    }
}p[maxn];
int seg[maxn];
int a[maxn],b[maxn];
int n; ll C;

/*
4
5 1001256456
1564 123
15641 4238
563 4156
489 847896
6351 546
2 -1002113415/24608 1000399497/24608
*/
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        
        map<pair<int,int>, int>mp;
        mp.clear();
        scanf("%d%lld",&n,&C);
        ll suma,sumb;
        suma = sumb = 0;
        for(int i = 1; i <= n; i++){
            scanf("%d%d",&w[i].a,&w[i].b);
            suma = suma+(-w[i].a);
            sumb = sumb+(-w[i].b); 
        }
        w[n+1].a = w[n+1].b = 0;
        sort(w+1,w+n+1);
    
        int cnt = 0,inf = 0,tol = 0;
        for(int i = 0; i <= n; i++){
            suma += 2*w[i].a;
            sumb += 2*w[i].b;
            if(suma == 0){
                if(sumb == C){
                    puts("-1");
                    inf = 1;
                    break;
                }
            }else{
                ll an1 = C-sumb;  //fenzi
                ll an2 = suma;        //fenmu
                
                ll g = gcd(an1,an2);
                if(g < 0){
                    if(an2 > 0){
                        an1 = an1/(-g);
                        an2 = an2/(-g);
                    }else{
                        an1 = an1/g;
                        an2 = an2/g;
                    }
                }else{
                    if(an2 < 0){
                        an1 = (-an1)/g;
                        an2 = (-an2)/g;
                    }else{
                        an1 = an1/g;
                            an2 = an2/g;
                    } 
                }
                if((ll)an1*(w[i].a) < (ll)an2*(-w[i].b)) continue;
                if((ll)an1*(w[i+1].a) > (ll)an2*(-w[i+1].b)) continue;
                if(!mp[make_pair(an1,an2)]){
                    p[tol].xa = an1;
                    p[tol++].xb = an2;
                    mp[make_pair(an1,an2)] = 1;
                }
            }
        }
        sort(p,p+tol);
        if(!inf){
            printf("%d", tol);
            for(int i = 0; i < tol; i++){
                int an1 = p[i].xa;
                int an2 = p[i].xb;
                printf(" %d/%d",an1,an2);
            }
            puts("");
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值