[CSP-S模拟测试96]题解

以后不能再借没改完题的理由不写题解了……

A.求和

求$\sum \sum i+j-1$

柿子就不化了吧……这年头pj都不考这么弱智的公式化简了……

坑点1:模数不定,可能没有2的逆元,那么只要先把乘数里的2去掉就好了。

坑点2:1e18炸long long $\rightarrow$ 慢速乘即可

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
typedef long long ll;
ll x,y,xx,yy,mod;
ll mul(ll a,ll b)
{
    ll res=0;
    while(b)
    {
        if(b&1)res+=a,res%=mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return res;
}
vector<ll> fac;
#define F
int main()
{
#ifdef F
    freopen("sum.in","r",stdin);
    freopen("sum.out","w",stdout);
#endif
    /*mod=1e9+7;
    cout<<mul(100000,3)<<endl;*/
    scanf("%lld%lld%lld%lld%lld",&x,&y,&xx,&yy,&mod);
    int cnt=1;
    fac.push_back(x+xx);
    fac.push_back(xx-x+1);
    fac.push_back(yy-y+1);
    for(int i=0;i<3;i++)
        if(cnt&&fac[i]%2==0)fac[i]/=2,cnt--;
    ll ans=1;
    for(int i=0;i<3;i++)
        ans=mul(ans,fac[i]);
    fac.clear();cnt=1;
    fac.push_back(y+yy);
    fac.push_back(xx-x+1);
    fac.push_back(yy-y+1);
    for(int i=0;i<3;i++)
        if(cnt&&fac[i]%2==0)fac[i]/=2,cnt--;
    ll res=1;
    for(int i=0;i<3;i++)

        res=mul(res,fac[i]);
    (ans+=res)%=mod;
    ans-=mul(xx-x+1,yy-y+1)%mod;
    (ans+=mod)%=mod;
    printf("%lld\n",ans);
    /*ans=(x+xx)*(xx-x+1)%mod*(yy-y+1)%mod*inv%mod;
    ans+=(xx-x+1)*(y+yy)%mod*(yy-y+1)%mod*inv%mod;ans%=mod;
    ans-=(xx-x+1)*(yy-y+1)%mod;ans=(ans+mod)%mod;
    printf("%lld\n",ans);*/
    return 0;
}

B.分组配对

显然,最大 $\times$ 最大$+$ 第二大 $\times$ 第二大 $+...$的方式能使小组总值最大。

这里简单写一下证明,考虑只有两对的情况。

设它们为$a,b,a-x,b-y$,因为实力值为正整数所以必有$a>x,b>y$

那么按排名配对的总值为$ab\times 2 -ay-bx+xy$

交叉配对的为$ab \times 2 -ay-bx$

很显然前者大于后者。

所以只要贪心地往右扫,到不能加入组里时开新组就好了。

暴力的话每次都sort或二分查找肯定是不行的

由于往右扩展这个过程具有单调性,可以考虑二分

但check一次的代价也很大,需要设法减小二分范围

那么可以先倍增到一个不合法的位置,再以$1<<(p-1)$为左边界,$1<<p$为右边界进行二分

时间复杂度最劣为$O(n\ log^2 \ n)$。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long ll;
ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch==‘-‘)f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-‘0‘,ch=getchar();
    return x*f;
}
const int N=5e5+5;
int n;
ll m;
ll a[N],b[N];
vector<ll> nowa,nowb;
bool check(int l,int r)
{
    vector<ll> tmpa,tmpb;
    swap(nowa,tmpa);swap(nowb,tmpb);
    for(int i=l;i<=r;i++)
        nowa.push_back(a[i]),nowb.push_back(b[i]);
    sort(nowa.begin(),nowa.end());sort(nowb.begin(),nowb.end());
    int sz=nowa.size();ll res=0;
    for(int i=sz-1;i>=0;i--)
    {
        res+=nowa[i]*nowb[i];
        if(res>m)return 0;
    }
    return 1;
}
int find(int x)
{
    int p=0;
    for(int i=0; ;i++)
    {
        p=i;
        if(!check(x,min(x+(1<<i)-1,n)))break;
        if(x+(1<<i)-1>=n)break;
    }
    int l=x+(1<<p-1)-1,r=min(x+(1<<p)-1,n),res=l;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(check(x,mid))res=mid,l=mid+1;
        else r=mid-1;
    }
    return res;
}
#define F
int main()
{
#ifdef F
    freopen("pair.in","r",stdin);
    freopen("pair.out","w",stdout);
#endif
    n=read();m=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    for(int i=1;i<=n;i++)
        b[i]=read();
    int ans=0;
    int i=1;
    while(i<=n)i=find(i)+1,ans++;
    printf("%d\n",ans);
    return 0;
}
/*
3 50
6 7 6
6 3 5
*/

C.城市游戏

原文地址:https://www.cnblogs.com/Rorschach-XR/p/11779769.html

时间: 2024-07-30 09:48:11

[CSP-S模拟测试96]题解的相关文章

csp-s模拟测试96

csp-s模拟测试96 $T1$一眼慢速乘,$T2$稍证一手最优性尝试用神奇数据结构优化,无果,弃.$T3$暴力+信仰. 100 03:16:38 95 03:16:56 35 03:17:10 230 03:17:10 比较意外. 和天皇$Skyh$同分祭. 起码可以伪证退役前我努力过. A. 求和 等差数列慢速乘,$/2$特判. B. 分组配对 二分右端点倍增优化. 考场把两个性能各异的暴力拼在一起,设一个参数,拿了$95$. C. 城市游戏 调不出来,咕. 神様(かみさま)ありがとう 运命

[CSP-S模拟测试59]题解

以后题解还是单独放吧. A.Divisors 根号筛求所有数的因子,扫一遍去重统计即可. #include<cstdio> #include<iostream> #include<cstring> #include<vector> #include<map> using namespace std; const int N=205; int a[N],m,n; map<int,int> bu; vector<int> re

CSP-S 模拟测试57题解

人生第一次A,B层一块考rank2,虽然说分差没几分,但还是值得纪念. 题解: T1 天空龙: 大神题,因为我从不写快读也没有写考场注释的习惯,所以不会做,全hzoi就kx会做,kx真大神级人物. T2 巨神兵: 大神题,一看数据范围这么小,我们考虑状压,最傻逼的暴力思路是压边,但是这显然不行.正解是压点,设$f[s]$为当前选定点集状态为$s$的方案数. 我们考虑转移,当前选定的点集肯定是可以通过边和没有连过来的点相连构成新的方案.所以转移所以我们考虑枚举补集的子集$k$,设$cnt$为s与k

[CSP-S模拟测试97]题解

A.小盆友的游戏 感觉题解解释的很牵强啊……还是打表找规律比较靠谱 对于每个人,它构造了一个期望函数$f(x)$,设它的跟班个数为$cnt[x]$,那么令$f(x)=2^{cnt[x]}-1$(??鬼知道为什么要等于这个) 然后再定义当前局面的期望函数为每个人期望函数之和. 然后你会发现每次猜拳后局面期望函数变化量都是1 那么期望步数其实就是终止局面期望函数值-初始局面期望函数值 $ans=2^{n-1}-1-\sum (2^{cnt[x]}-1)$ #include<bits/stdc++.h

[CSP-S模拟测试53]题解

A.u 只涉及到区间修改可以考虑差分,然而如果每一行都差分复杂度还是过高.我们发现差分标记也是连续的(一行横着的一行斜着的),所以可以维护两个 差分的差分,扫两遍统计即可. #include<cstdio> #include<iostream> #include<cstring> using namespace std; typedef long long ll; const int N=2005; int read() { int x=0,f=1;char ch=ge

[CSP-S模拟测试60]题解

回去要补一下命运石之门了…… A.嘟嘟噜 给定报数次数的约瑟夫,递推式为$ans=(ans+m)\% i$. 考虑优化,中间很多次$+m$后是不用取模的,这种情况就可以把加法变乘法了.问题在于如何找到下一次需要取模的位置. 解不等式$ans+km \ge i+k$即可,需要处理一下边界. 据说可以证明复杂度是$O(m \log n)$的,但我不是很会. //考场代码 稍丑 #include<bits/stdc++.h> using namespace std; typedef long lon

[CSP-S模拟测试63]题解

A.Median 这题的数据生成方式并没有什么规律,所以可以认为是随机数据. 维护一个桶,表示当前K长区间里的值域情况. 并且用变量记录中位数值域上的左侧有多少个数,当区间调整时一并调整桶和这个变量即可. 由于是随机数据,所以每次的调整幅度并不会很大,近似于常数. 复杂度$O(n)$. #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespa

CSP-S模拟测试69 题解

一如既往的垃圾,又回到了那个场场垫底的自己,明明考场上都想到正解了,但是就是拿不到分,可能是互奶把rp用光了吧以后一定加强训练代码能力. T1: 考场上一直yy矩阵快速幂,虽然自己矩阵快速幂一点都不会还是硬着头皮yy,发现不可做之后并没有及时转化思路,但其实自己预处理的数组就是正解. 切记:不仅矩阵快速幂是log的,普通快速幂也是2333 然后这题其实很水啊,我们设$dp[i][j]$为前$i$列放$j$个棋子的方案数,然后枚举最后一列放多少个棋子就好了. 转移方程为$dp[i][j]=\sum

[CSP-S模拟测试74]题解

A.梦境 如果不用去重一定要用Multiset……挂30分算是出题人手下留情了. 贪心.把点排序,区间按右端点递增排序.依次考虑每个区间,取能选的最靠左的点即可.multiset维护. #include<cstdio> #include<iostream> #include<cstring> #include<set> #include<algorithm> using namespace std; int read() { int x=0,f=