2019暑期训练—牛客网第九场部分题解(B,D,E,H,J)

B Quadratic equation

题意:
(x+y)%p=b;
(xy)%p=c;
输入b,c,求x和y。
思路:
即 x+y=kp+b,x
y=kp+c。
关键在于求二次剩余。
如果p % 4 =3,x^2 % p = a •,那么x = ±pow(a, (p+1)/4, p)。
还需要判断一下是不是有解。△>0。
(其实直接套板子也是可以的,只不过代码比较长。)
AC代码

#include<bits/stdc++.h>
#include<algorithm>
#include<complex>
#include<iostream>
#include<iomanip>
#include<ostream>
#include<cstring>
#include<string.h>
#include<string>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<cstdlib>
#include<time.h>
#include<ctime>
#include<bitset>
// #include<ext/pb_ds/assoc_container.hpp>
// #include<ext/pb_ds/hash_policy.hpp>
using namespace std;
// using namespace __gnu_pbds;
#define pb push_back
#define _fileout freopen("C:\\Users\\zsk18\\Desktop\\out.txt","w",stdout)
#define _filein freopen("C:\\Users\\zsk18\\Desktop\\in.txt","r",stdin)
#define ok(i) printf("ok%d\n",i)
#define gcd(a,b) __gcd(a,b) ;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>PII;
const double PI = acos(-1.0);
const ll MOD=1e9+7;
const ll NEG=1e9+6;
const int MAXN=1e6+10;
const int INF=0x3f3f3f3f;
const ll ll_INF=9223372036854775807;
const double eps=1e-9;
ll qm(ll a,ll b){ll ret=1;while(b){if(b&1)ret=ret*a%MOD;a=a*a%MOD;b>>=1;}return ret;}
ll Inv(ll x){return qm(x,MOD-2);}
int main()
{
    int T;
    cin >> T;
    while(T--) 
    {
        ll b, c;
        cin >> b >> c;
        ll det = b * b - 4 * c;
        det %= MOD;
        if (det < 0) det += MOD;
        if (qm(det, (MOD - 1) / 2) == MOD - 1) {
            printf("-1 -1\n");
        }
        else {
            ll r = qm(det, (MOD + 1) / 4);
            ll f1 = (b + r) % MOD, f2 = (b - r) % MOD;
            ll bk = (MOD + 1) / 2;
            f1 = f1 * bk % MOD, f2 = f2 * bk % MOD;
            if (f1 < 0) f1 += MOD;
            if (f2 < 0) f2 += MOD;
            if (f1 > f2) swap(f1, f2);
            printf("%lld %lld\n",f1,f2);
        }
    }
    return 0;
}

D Knapsack Cryptosystem

题意:
给定一个长度为n的数组和一个数s,求数组的一个子集,使子集的和等于s。输出01序列,其中0表示对应位置的数没选,1表示选了。其中n<=36, s<=9e18
思路:
直接搜索剪枝会T,对于前一半序列,暴力搜素出所有可能的答案,并记录路径(状压表示即可),对于后一半的序列爆搜出所有可能的结果,同样状压记录路径,最后在前一半的搜索中看有没有对应的解即可。然后打印出可以匹配的状态即可(位数不够的需要补0)。总的时间复杂度为O(2^18)。
AC代码:

#include<bits/stdc++.h>
#include<algorithm>
#include<complex>
#include<iostream>
#include<iomanip>
#include<ostream>
#include<cstring>
#include<string.h>
#include<string>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<cstdlib>
#include<time.h>
#include<ctime>
#include<bitset>
// #include<ext/pb_ds/assoc_container.hpp>
// #include<ext/pb_ds/hash_policy.hpp>
using namespace std;
// using namespace __gnu_pbds;
#define pb push_back
#define _fileout freopen("C:\\Users\\zsk18\\Desktop\\out.txt","w",stdout)
#define _filein freopen("C:\\Users\\zsk18\\Desktop\\in.txt","r",stdin)
#define ok(i) printf("ok%d\n",i)
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>PII;
const double PI = acos(-1.0);
const ll MOD=1e9+7;
const ll NEG=1e9+6;
const int MAXN=1e6+10;
const int INF=0x3f3f3f3f;
const ll ll_INF=9223372036854775807;
const double eps=1e-9;
ll qm(ll a,ll b){ll ret=1;while(b){if(b&1)ret=ret*a%MOD;a=a*a%MOD;b>>=1;}return ret;}
ll Inv(ll x){return qm(x,MOD-2);}
struct S
{
    int f;
    ull state;
};
int n;
ull a[MAXN];
ull s;
map<ull,S>m;
// vector<S>v;
int middle;
void print(ull x,int f)
{
    int number=0;
    while(x)
    {
        printf("%llu",x%2);
        x/=2;
        number++;
    }
    if(f==1)while(number++<middle)printf("0");//补0
    else while(number++<(n-middle))printf("0");//补0
}
int flag=0;
void dfs1(ull val,ull state,int now,int end)
{
    if(now==end)
    {
        // printf("val=%llu state=%llu\n",val,state);
        m[val].f=1;
        m[val].state=state;
        val+=a[now];
        state=state|(1<<(now-1));
        // printf("val=%llu state=%llu\n",val,state);
        m[val].f=1;
        m[val].state=state;
        return;
    }
    dfs1(val+a[now],state|(1<<(now-1)),now+1,end);
    dfs1(val,state,now+1,end);
    return;
}
void dfs2(ull val,ull state,int now,int end)
{
    if(flag)return;
    // printf("now=%d\n",now);
    if(now==end)
    {
        // printf("val=%llu state=%llu\n",val,state);
        ull mid=s-val;
        if(m[mid].f==1)
        {
            print(m[mid].state,1);
            print(state,2);
            flag=1;
            return;
        }
        val+=a[now];
        state|=(1<<(now-1-middle));
        mid=s-val;
        // printf("val=%llu state=%llu\n",val,state);
        if(m[mid].f==1)
        {
            print(m[mid].state,1);
            print(state,2);
            flag=1;
            return;
        }
        return;
    }
    dfs2(val+a[now],state|(1<<(now-1-middle)),now+1,end);
    dfs2(val,state,now+1,end);
}
int main()
{
    scanf("%d%llu",&n,&s);
    for(int i=1;i<=n;i++)
    {
        scanf("%llu",&a[i]);
    }
    middle=n/2;
    dfs1(0,0,1,middle);
    // ok(1);
    dfs2(0,0,middle+1,n);

    return 0;
}

E All men are brothers

题意:
n个点,起始没有边,每次可以选择其中两点之间连一条边,每次连边,都需要输出任选四个点两两之间不连通的方案数。
思路:
并查集维护连通块儿,size记录连通块儿大小。然后考虑到答案是非增的,即每次的答案可以从上一次的答案中减去一个值得到。这个值就是上次的方案数中四个点中有两个点在这一次连接的两个连通块儿中的方案数。即ans[i]=ans[i-1]-size[x] *size[y] *s,其中s表示剩余的块儿中选择两个不在同一个块儿中的点的方案数。用sum维护目前状态选择不在同一个连通块儿的两个点的方案数,每次更新答案ans和sum即可。注意要用ull,因为会爆ll。
AC代码:

#include<bits/stdc++.h>
#include<algorithm>
#include<complex>
#include<iostream>
#include<iomanip>
#include<ostream>
#include<cstring>
#include<string.h>
#include<string>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<cstdlib>
#include<time.h>
#include<ctime>
#include<bitset>
// #include<ext/pb_ds/assoc_container.hpp>
// #include<ext/pb_ds/hash_policy.hpp>
using namespace std;
// using namespace __gnu_pbds;
#define pb push_back
#define _fileout freopen("C:\\Users\\zsk18\\Desktop\\out.txt","w",stdout)
#define _filein freopen("C:\\Users\\zsk18\\Desktop\\in.txt","r",stdin)
#define ok(i) printf("ok%d\n",i)
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>PII;
const double PI = acos(-1.0);
const ll MOD=1e9+7;
const ll NEG=1e9+6;
const int MAXN=1e6+10;
const int INF=0x3f3f3f3f;
const ll ll_INF=9223372036854775807;
const double eps=1e-9;
ll qm(ll a,ll b){ll ret=1;while(b){if(b&1)ret=ret*a%MOD;a=a*a%MOD;b>>=1;}return ret;}
ll Inv(ll x){return qm(x,MOD-2);}
ull sum;
ull ans;
int fa[MAXN];
ull size[MAXN];
ull n,m;
int get(int x)
{
    if(x==fa[x])return x;
    return fa[x]=get(fa[x]);
}  
int main()
{
    cin>>n>>m;
    for(int i=1;i<=(int)n;i++)
    {
        fa[i]=i;
        size[i]=1;
        // cout<<i<<endl;
    }
    ans=n*(n-1)/2*(n-2)/3*(n-3)/4;
    sum=n*(n-1)/2;
    // cout<<ans<<" "<<sum<<endl;
    cout<<ans<<endl;
    while(m--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        x=get(x),y=get(y);
        if(x==y)
        {
            cout<<ans<<endl;
        }
        else
        {
            ull mid=sum;
            // printf("sum=%I64u\n",sum);
            ull a=size[x]*(n-size[x]-size[y]);
            ull b=size[y]*(n-size[x]-size[y]);
            ull c=size[x]*size[y];
            mid-=(a+b+c);
            ans-=(mid*size[x]*size[y]);
            sum-=size[x]*size[y];
            cout<<ans<<endl;
            fa[x]=y;
            size[y]+=size[x];
        }
    }
    return 0;
}

H Cutting Bamboos

** 题意:**
给定一排n颗竹子各种的高度,有多次查询,每次查询(l,r,x,y),表示砍掉把l~r这个区间的竹子砍为高度为0,一共砍y刀,每次砍去的竹子的长度和相同,问第x刀应该再距离高度多远的地方砍。每次查询无需修改竹子的高度。
思路:
先考虑到每次砍掉的竹子的总高度一定是l~r区间竹子高度和除以y。然后砍了x次之后,剩余竹子的高度和一定是(sum( r)-sum(l-1)) - (sum( r)-sum(l-1))*x/y。所以只需要二分查找第x次砍的高度,查询区间高度小于mid的竹子高度之和以及区间高度大于mid的竹子的数量,可以找到剩余的竹子的高度,如果高度等于(sum( r)-sum(l-1)) - (sum( r)-sum(l-1))*x/y,就是答案。
用主席树来维护即可。
AC代码:

#include<bits/stdc++.h>
#include<algorithm>
#include<complex>
#include<iostream>
#include<iomanip>
#include<ostream>
#include<cstring>
#include<string.h>
#include<string>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<cstdlib>
#include<time.h>
#include<ctime>
#include<bitset>
// #include<ext/pb_ds/assoc_container.hpp>
// #include<ext/pb_ds/hash_policy.hpp>
using namespace std;
// using namespace __gnu_pbds;
#define pb push_back
#define _fileout freopen("C:\\Users\\zsk18\\Desktop\\out.txt","w",stdout)
#define _filein freopen("C:\\Users\\zsk18\\Desktop\\in.txt","r",stdin)
#define ok(i) printf("ok%d\n",i)
#define gcd(a,b) __gcd(a,b) ;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>PII;
const double PI = acos(-1.0);
const ll MOD=1e9+7;
const ll NEG=1e9+6;
const int MAXN=2e5+10;
const int INF=0x3f3f3f3f;
const ll ll_INF=9223372036854775807;
const double eps=1e-9;
ll qm(ll a,ll b){ll ret=1;while(b){if(b&1)ret=ret*a%MOD;a=a*a%MOD;b>>=1;}return ret;}
ll Inv(ll x){return qm(x,MOD-2);}
struct node 
{
    int l, r;
    ll sum, num;
}tree[MAXN * 22];
int n, q;
int h[MAXN];
int root[MAXN], tot;
ll sum[MAXN];
int update(int pre, int l, int r, int pos) 
{
    int cur = ++tot;
    tree[cur].l = tree[pre].l;
    tree[cur].r = tree[pre].r;
    tree[cur].sum = tree[pre].sum;
    tree[cur].num = tree[pre].num;
    tree[cur].sum += pos;
    tree[cur].num++;
    if(l == r) return cur;
    int mid = (r + l) >> 1;
    if(pos <= mid) tree[cur].l = update(tree[pre].l, l, mid, pos);
    else tree[cur].r = update(tree[pre].r, mid + 1, r, pos);
    return cur;
}
ll cntsum, cntnum;
void query(int pl, int pr, int l, int r, int pos) 
{
    if(pos <= l) 
    {
        cntsum += tree[pr].sum - tree[pl].sum;
        cntnum += tree[pr].num - tree[pl].num;
        return;
    }
    int mid = (r + l) >> 1;
    if(pos <= mid) query(tree[pl].l, tree[pr].l, l, mid, pos);
    query(tree[pl].r, tree[pr].r, mid + 1, r, pos);
}
int main() {
    int l, r, x, y;
    int ll, rr, mid, ans;
    double cnt, ans_;
    scanf("%d %d", &n, &q);
    for(int i = 1; i <= n; i++) 
    {
        scanf("%d", &h[i]);
        sum[i] = sum[i - 1] + h[i];
        root[i] = update(root[i - 1], 1, 100000, h[i]);
    }
    double tmp;
    while(q--) {
        scanf("%d %d %d %d", &l, &r, &x, &y);
        cnt = 1.0 * (sum[r] - sum[l - 1])  * x / y;
          
        ll = 1, rr = 100000;
        while(ll <= rr)
        {
            mid = (rr + ll ) >> 1;
            cntsum = 0;
            cntnum = 0;
            query(root[l - 1], root[r], 1, 100000, mid);
            if(cntsum - cntnum * (mid - 1) >= cnt)
            {
                ans = mid;
                ans_ = cntsum - cntnum * (mid - 1);
                tmp = cntnum;
                ll = mid + 1;
            } 
            else 
            {
                rr = mid - 1;
            }
        }
          
        if(ans != 100000) 
        {
            cntsum = 0, cntnum = 0;
            query(root[l - 1], root[r], 1, 100000, ans + 1);
            ans_ = cntsum - cntnum * ans;
        } 
        else 
        {
            ans_ = 0;
        }
        printf("%.15f\n", ans - (cnt - ans_) / tmp);
    }
    return 0;
}

J Symmetrical Painting

题意:
给一系列的矩形,第i个矩形的左下顶点是(i-1,Li),右上顶点是(i,Ri)。在这些矩形覆盖的面积中,找到一个最大的图形,使其是水平方向的轴对称图形。输出最大轴对称图形的面积。(单位正方形的面积为1)。
思路:
首先大胆猜测对称轴一定位于某一个矩形的中线(找不到反例就这样认为了)。其次可以发现,对于一个对称轴y0,每一个矩形的贡献是min(r-y0,y0-l)。所以UI与每一个矩形,当y0<=l或者y0>=r的时候,贡献为0,在矩形内部的时候,贡献先增后减。对于每个矩形维护最大值,并且扫一遍就可以了。
AC代码

#include<bits/stdc++.h>
#include<algorithm>
#include<complex>
#include<iostream>
#include<iomanip>
#include<ostream>
#include<cstring>
#include<string.h>
#include<string>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<cstdlib>
#include<time.h>
#include<ctime>
#include<bitset>
// #include<ext/pb_ds/assoc_container.hpp>
// #include<ext/pb_ds/hash_policy.hpp>
using namespace std;
// using namespace __gnu_pbds;
#define pb push_back
#define _fileout freopen("C:\\Users\\zsk18\\Desktop\\out.txt","w",stdout)
#define _filein freopen("C:\\Users\\zsk18\\Desktop\\in.txt","r",stdin)
#define ok(i) printf("ok%d\n",i)
#define gcd(a,b) __gcd(a,b) ;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>PII;
const double PI = acos(-1.0);
const ll MOD=1e9+7;
const ll NEG=1e9+6;
const int MAXN=3e6+10;
const int INF=0x3f3f3f3f;
const ll ll_INF=9223372036854775807;
const double eps=1e-9;
ll qm(ll a,ll b){ll ret=1;while(b){if(b&1)ret=ret*a%MOD;a=a*a%MOD;b>>=1;}return ret;}
ll Inv(ll x){return qm(x,MOD-2);}
struct rec
{
    ll l, r;
    bool operator <(const rec &a){
        return l<a.l;
    }
}t[MAXN];
int main()
{
    int n, cnt = 0;
    scanf("%d", &n);
    for(int i = 0; i < n; ++i)
    {
        ll l, r;
        scanf("%lld%lld", &l, &r);
        l *= 2, r *= 2;
        t[++cnt].l = l, t[cnt].r = 1;
        t[++cnt].l = (l+r) >> 1, t[cnt].r = -2;//过了对称轴之后面积会变小
        t[++cnt].l = r, t[cnt].r = 1;
    }
    sort(t+1,t+cnt+1);
    ll ans = 0, ma = 0, tmp = 0, pre = 0;
    for(int i = 1; i < cnt; ++i)
    {
        pre += t[i].r;
        tmp = t[i+1].l - t[i].l;
        ma += pre * tmp;
        ans = max(ans, ma);
    }
    printf("%lld\n", ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值