心得
多校即将结束,感觉人家在变强,我们在原地踏步,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;
}