2019牛客暑期多校训练营(第九场)(A、B、D、E、H、I、J)

心得

多校即将结束,感觉人家在变强,我们在原地踏步,gg

A The power of Fibonacci(斐波那契循环节+中国剩余定理)

中国剩余定理,这里不一定非要用

可以采用在一个的余数上加另一个模数的倍数的写法,

即暴力的扩展欧几里得,来找到满足第二个的根

时间宝贵,建议预处理斐波那契循环节,现处理容易超时

#include<bits/stdc++.h>
using namespace std;
const int N=1e7+10;
const int p=1e9;
int mod[2];
long long ans[2];
int f[N],n,m;
int modpow(int x,int n,int mod)
{
    int res=1;
    for(;n;n/=2,x=1ll*x*x%mod)
        if(n&1)res=1ll*x*res%mod;
    return res;
}
int main()
{
    mod[0]=8*8*8;
    mod[1]=125*125*125;
    f[0]=0;f[1]=1;
    scanf("%d%d",&n,&m);
    for(int k=0;k<2;++k)
    {
        int j;
        for(j=2;;++j)
        {
            f[j]=(f[j-1]+f[j-2])%mod[k];
            if(f[j]==0&&f[j-1]==1)break;
        }
        for(int i=0;i<j;++i)
        {
            int num=n/j;
            if(n%j>=i)num++;
            ans[k]=(ans[k]+1ll*num*modpow(f[i],m,p))%mod[k];
        }
    }
    while(ans[1]%mod[0]!=ans[0])ans[1]+=mod[1];
    printf("%lld\n",ans[1]);
    return 0;
}

B Quadratic equation(二次剩余)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int p=1e9+7;
int t;
ll b,c,x,y,w,del,inv2;
struct field{//扩域 x实部 y虚部
	ll x,y;
	field(ll a=0,ll b=0){
		x=a;y=b;
	}
};
field operator*(field a,field b){return field(1ll*a.x*b.x%p+1ll*a.y*b.y%p*1ll*w%p,1ll*a.x*b.y%p+1ll*a.y*b.x%p);}
 
ll ran(){//随机数种子
	static ll seed=23333;
	return seed=((((((ll)seed^20030927)%p+330802)%p*9410)%p-19750115+p)%p^730903)%p;
}
 
ll pows(ll a,ll b){
	ll base=1;
	while(b){
		if(b&1) base=1ll*base*a%p;
		a=1ll*a*a%p;b/=2;
	}
	return base;
}
 
field powfield(field a,ll b){//扩域快速幂
	field base=field(1,0);
	while(b){
		if(b&1) base=base*a;
		a=a*a;b/=2;
	}
	return base;
}
 
ll legander(ll x){//勒让德记号 
//返回-1时x不为二次剩余
//返回1时x为二次剩余
//返回0时p整除x
	ll a=pows(x,(p-1)/2);
	if(a+1==p) return -1;
	return a;
}
 
ll surplus(ll x){//求b.x*b.x==x(mod p)时的b.x
//即给定x,求x为二次剩余时的解b.x
//x为非二次剩余时返回0 while(1)期望次数两次
	ll a;
	if(legander(x)==-1) return -1;
	while(1){
		a=ran()%p;
		w=((1ll*a*a-x)%p+p)%p;
		if(legander(w)==-1) break;
	}
	field b=field(a,1);
	b=powfield(b,(p+1)/2);
	return b.x;
}
int main()
{
	inv2=(p+1)/2;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%lld%lld",&b,&c);
		ll v=((b*b-4*c)%p+p)%p;
		del=v?surplus(v):0;
		if(del==-1){puts("-1 -1");continue;}
		x=(((b-del)*inv2)%p+p)%p;y=(((b+del)*inv2)%p+p)%p;
		if(x>y)swap(x,y);
		printf("%lld %lld\n",x,y);
	}
	return 0;
}

D Knapsack Cryptosystem(折半枚举)

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int N=37;
ull s,a[N];
int n,sz,mx;
ull sum[(1<<18)+10],sum2[(1<<18)+10];
map<ull,int>vis;
void solve(int n)
{
    sz=n/2;mx=(1<<sz);
    for(int i=1;i<mx;++i)
    {
        int pos=__builtin_ffs(i);pos--;
        sum[i]=sum[i-(1<<pos)]+a[pos];
        if(sum[i]==s)
        {
            for(int j=0;j<sz;++j)
            if(i>>j&1)putchar('1');
            else putchar('0');
            for(int j=0;j<n-sz;++j)
            putchar('0');
            return;
        }
        vis[sum[i]]=i;
    }
    mx=(1<<(n-sz));
    for(int i=1;i<mx;++i)
    {
        int pos=__builtin_ffs(i);pos--;
        sum2[i]=sum2[i-(1<<pos)]+a[sz+pos];
        if(sum2[i]==s)
        {
            for(int j=0;j<sz;++j)
            putchar('0');
            for(int j=0;j<n-sz;j++)
            if(i>>j&1)putchar('1');
            else putchar('0');
            return;
        }
        else if(vis.count(s-sum2[i]))
        {
            int v=vis[s-sum2[i]];
            for(int j=0;j<sz;j++)
            if(v>>j&1)putchar('1');
            else putchar('0');
            for(int j=0;j<n-sz;j++)
            if(i>>j&1)putchar('1');
            else putchar('0');
            return;
        }
    }
}
int main()
{
    scanf("%d%llu",&n,&s);
    for(int i=0;i<n;++i)
    scanf("%llu",&a[i]);
    solve(n);
    puts("");
    return 0;
}

E All men are brothers(并查集)

是签到题,但我签了D之后,发现并不会这题,

看了题解之后,发现我计数还是太菜了……

注意n为1e5,C(n,4)直接乘了再除会爆ll,

可以用先除以6的ull写法,或直接预处理

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,m,par[N],x,y;
ll sz[N],ans,sum,v;
ll c[N][5];
int find(int x)
{
	return par[x]==x?x:par[x]=find(par[x]);
}
void init(int n)
{
	for(int i=0;i<=n;++i)
	{
		c[i][0]=1;
		for(int j=1;j<=min(i,4);++j)
		c[i][j]=c[i-1][j]+c[i-1][j-1];
	}
}
ll C(ll n,ll m)
{
	assert(m<=4);
	if(m<0||n<m)return 0;
	return c[n][m];
}
int main()
{
	scanf("%d%d",&n,&m);
	init(n);
	for(int i=1;i<=n;++i)
	{
		par[i]=i;
		sz[i]=1;
		sum+=C(sz[i],2);//ban掉同一集合里的方案 
	}
	ans=C(n,4);
	printf("%lld\n",ans);
	for(int i=1;i<=m;++i)
	{
		scanf("%d%d",&x,&y);
		x=find(x),y=find(y);
		if(x!=y)
		{	
			v=C(n-sz[x]-sz[y],2);//其它集合任取两个
			v-=(sum-C(sz[x],2)-C(sz[y],2));//去掉在其他集合的同一集合里取的两个 
			ans-=sz[x]*sz[y]*v;
			sum=sum-C(sz[x],2)-C(sz[y],2);
			par[y]=x,sz[x]+=sz[y],sum+=C(sz[x],2);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

H Cutting Bamboos(主席树)

主席树维护一个sum,离散化后仍然把原值插到对应位置,然后套一个二分

时限给的5秒,在主席树外面写二分是两个log跑了3秒,

里面写是一个log,看别的大佬的代码不到1秒,tql

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=2e5+10;
const double eps=1e-8;
typedef long long ll;
struct node
{
	int l,r,num;
	ll sum;
}e[maxn*40];
//1e5开*40,1e6开*45
//其实开(log(maxn)*(n+m)*maxn)就好
int n,q,mx;
int a[maxn],rk[maxn],X[maxn],tot;
int root[maxn],cnt;//开m+1的规模 
int L,R,x,y;
double l,r,sum;
void update(int l,int r,int &cur,int las,int pos,ll v)
{
	cur=++cnt;
    //直接一路搜到底更新 不利用回溯pushup 单路单点修改
	e[cur]=e[las];
	e[cur].num++;
	e[cur].sum+=v;
	if(l==r)return;
	int mid=(l+r)/2;
	if(pos<=mid)update(l,mid,e[cur].l,e[las].l,pos,v);
	else update(mid+1,r,e[cur].r,e[las].r,pos,v); 
}
ll query(int l,int r,int cur,int las,int ql,int qr)//询问值域在[ql,qr]间的和 
{
	if(ql>qr)return 0;
    //除了两棵树作差以外 就是裸的线段树区间求和
	if(ql<=l&&r<=qr)return e[cur].sum-e[las].sum;
	ll ans=0;
	int mid=(l+r)/2;
	if(ql<=mid)ans+=query(l,mid,e[cur].l,e[las].l,ql,qr);
	if(qr>mid)ans+=query(mid+1,r,e[cur].r,e[las].r,ql,qr);
	return ans;
}
ll query2(int l,int r,int cur,int las,int ql,int qr)//询问值域在[ql,qr]间的和 
{
	if(ql>qr)return 0;
    //除了两棵树作差以外 就是裸的线段树区间求和
	if(ql<=l&&r<=qr)return e[cur].num-e[las].num;
	ll ans=0;
	int mid=(l+r)/2;
	if(ql<=mid)ans+=query2(l,mid,e[cur].l,e[las].l,ql,qr);
	if(qr>mid)ans+=query2(mid+1,r,e[cur].r,e[las].r,ql,qr);
	return ans;
}
double cal(int L,int R,double x)
{
	int v=(int)x,pos;
	pos=upper_bound(X+1,X+tot+1,v)-X;
	pos--;//注意pos=0或-1 
	ll s1=query(1,tot,root[R],root[L-1],1,pos);
	ll n1=query2(1,tot,root[R],root[L-1],1,pos);
	//printf("x:%.7lf s1:%lld n1:%lld num:%d\n",x,s1,n1,R-L+1);
	double ans=((R-L+1)-n1)*x+s1;
	return ans;
}
int main()
{
	 e[0].l=e[0].r=e[0].num=0;e[0].sum=0;
	 root[0]=0;
	 scanf("%d%d",&n,&q);
	 cnt=0;
	 memset(e,0,sizeof e);
	 for(int i=1;i<=n;++i)
	 {
	  scanf("%d",&a[i]);
	  mx=max(a[i],mx);
	  X[++tot]=a[i];
     }
     sort(X+1,X+tot+1);
     tot=unique(X+1,X+tot+1)-(X+1);
	 for(int i=1;i<=n;++i)
	 {
	 	rk[i]=lower_bound(X+1,X+tot+1,a[i])-X;
	 	update(1,tot,root[i],root[i-1],rk[i],a[i]);
	 }
	 while(q--)
	 {
	 	scanf("%d%d%d%d",&L,&R,&x,&y);
	 	l=0;r=mx;
		sum=query(1,tot,root[R],root[L-1],1,tot)*1.0/y*(y-x);//砍完第x刀 只剩y-x刀的总高度 
	 	//printf("sum:%7lf\n",sum);
		while(r-l>eps)
	 	{
	 		double mid=(l+r)/2;
	 		if(cal(L,R,mid)<sum)l=mid;
	 		else r=mid;
	 	}
	 	printf("%.7lf\n",l);
	 } 
	 return 0;
}

I KM and M(类欧)

类欧板子题,比赛的时候卡J题了没看到,woc

#include<cstring>
#include<cstdio>
#define ll __int128
#define ull unsigned long long
using namespace std;
const int mod=1e9+7;

ll n,m,ans;

ull x,y;

int res;

ll f(ll a,ll b,ll c,ll n){
    if(a==0){
        return (n+1)*(b/c);
    }
    if(a<c && b<c){
        ll m=(a*n+b)/c;
        if(m==0)return 0;
        return n*m-f(c,c-b-1,a,m-1);
    }
    return f(a%c,b%c,c,n)+(n+1)*(b/c)+((n+1)/(1+(n&1)))*(n/(2-(n&1)))*(a/c);
}
 
int main()
{
		scanf("%llu%llu",&x,&y);
		n=x;m=y;
        for(ll lo=1;lo<=n*m;lo=lo+lo)
		if(m&lo)ans+=(f(m,m,lo,n-1)-f(m,m,lo+lo,n-1)*2)*lo;
		res=ans%mod;
		printf("%d\n",res);
}

J Symmetrical Painting(差分)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e5+7;
int x[maxn],y[maxn];
int vec[maxn*7],tot;
ll cnt[maxn*7],a,v,s,ans;
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&x[i],&y[i]);
        x[i]*=2;y[i]*=2;
        vec[++tot]=x[i];
        vec[++tot]=x[i]+1;
        vec[++tot]=x[i]/2+y[i]/2+1;
        vec[++tot]=y[i];
		vec[++tot]=y[i]+1;
    }
    sort(vec+1,vec+tot+1);
    tot=unique(vec+1,vec+tot+1)-(vec+1);
    for(int i=1;i<=n;i++)
    {
    	int pos1=lower_bound(vec+1,vec+tot+1,x[i]+1)-vec;
    	int pos2=lower_bound(vec+1,vec+tot+1,x[i]/2+y[i]/2+1)-vec;
    	int pos3=lower_bound(vec+1,vec+tot+1,y[i]+1)-vec;
        cnt[pos1]+=1;
        cnt[pos2]-=2;
        cnt[pos3]+=1;
    }
    for(int i=1;i<=tot;i++)
    { 
    	//printf("pos[%d]:%lld\n",i,vec[i]);
    	a=cnt[i];
    	v+=a;
    	s+=v;
    	//printf("s:%lld\n",s);
    	ans=max(ans,s);
    	if(i+1<=tot)s+=v*(vec[i+1]-vec[i]-1);
    	ans=max(ans,s);
    	//printf("s:%lld\n",s);
    }
    printf("%lld\n",ans);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值