8.1省选模拟总结

考的时候看错第一题的。。。
只考了64分
第一题 模积和
求∑∑((n mod i)*(m mod j))其中1<=i<=n,1<=j<=m,i≠j。
注意,i!=j,当i=j时,我们可以用 2n 的负杂度求出(就是找nmodi、mmodj的共同循环)

那么怎么去掉i=j的东西呢?
我们可以看到i=j的部分为:
(n%i)*(m%i)
然后转成:
(n- n/i i)(m m/i *i)
这时我们可以拆开,先算常数部分,再用和之前类似的方法算剩余的。
贴代码:

#include<algorithm>
#include<cstdio>
#include<cmath>
using namespace std;
#define MOD 19940417
#define INE 6646806
int ans,n,m,x,y;
void init(){
    scanf("%d %d",&n,&m);
}
int get(int x){
    static int s,y,xx,yy;
    s=sqrt(x);
    y=0,xx=x;
    for (int i=2;i<=s;i++){
        yy=x/i;
        y=(y+(long long)(xx-yy)*(x+x-(xx+yy+1)*(i-1))/2%MOD+x%(i-1))%MOD;
        xx=yy;
    }
    return (y+(long long)(xx-s)*(x+x-(xx+s+1)*s)/2%MOD+x%s)%MOD;
}
int sum(int l,int r){
    return (long long)(l+r)*(r-l+1)/2%MOD;
}
int sum2(int x){
    return ((long long)x*x%MOD*x-x+(long long)3*sum(1,x)+MOD)%MOD*INE%MOD;
}
int calc(int l,int r){
    return (sum2(r)-sum2(l-1)+MOD)%MOD;
}
void work(){
    static int s1,s2,s3;
    if (n>m)swap(n,m);
    ans=((long long)get(n)*get(m)%MOD-(long long)n*m%MOD*n%MOD+MOD)%MOD;
    for (int i=1,j;i<=n;i=j+1){
        j=min(n/(n/i),m/(m/i));
        s1=((long long)m*(n/i)%MOD+(long long)n*(m/i)%MOD)%MOD*sum(i,j)%MOD;
        s2=(long long)(n/i)*(m/i)%MOD*calc(i,j)%MOD;
        ans=((long long)ans-s2+s1+MOD)%MOD;
    }
}
void write(){
    printf("%d",ans);
}
int main(){
    init();
    work();
    write();
    return 0;
}

第二题 长跑
暴力拿了30分
题目大意:
给定n个点,每个点有个点权,
有三类事件:
1.连接a、b(无向边、无重边)
2.将a的权值改为b
3.询问从a到b最大点权和(同一条边只能由同一个方向通过,可重复走点,但点权只加一次)

显然,若路径上出现环,那么肯定存在一种选边方向的方案,使得该环一定被走完,那么我们可以只考虑没有环的情况(缩点),由于为单向通过边,那么最大值为a到lca(a,b)到b的权值和

1中动态连边,我们可以用动态树维护。
然而,我们可以发现,按读入顺序建最小生成树,其实不影响最后缩点后树的结构(缩点时链剖向上跳,将路径上的所有点权推到lca上就可以了)

于是我们可以得到一个比较优美的方法
贴代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 150001
using namespace std;
int n,m,sum;
int fa[N][18],b[N],f[N*4],deep[N],a[N*2][2],q[N*5][3],bz[N],r[N],v[N],bz1[N],h[N],u[N],g[N];
void init(){
    scanf("%d %d",&n,&m);
    for (int i=1;i<=n;i++)scanf("%d",&v[i]),u[i]=v[i],b[i]=h[i]=i;
    for (int i=1;i<=m;i++)
        scanf("%d %d %d",&q[i][0],&q[i][1],&q[i][2]);
}
int get(int x){return x==b[x]?x:b[x]=get(b[x]);}
int geth(int x){return x==h[x]?x:h[x]=geth(h[x]);}
void ins(int x,int y){
    static int sum=0;
    a[++sum][0]=y,a[sum][1]=g[x],g[x]=sum;
}
void dfs(int x){
    static int sum=0,y;
    bz1[bz[x]=++sum]=x;
    for (int i=0;y=fa[fa[x][i]][i];fa[x][++i]=y);
    deep[x]++;
    for (int i=g[x];i;i=a[i][1])
        if (!bz[a[i][0]])
            fa[a[i][0]][0]=x,deep[a[i][0]]=deep[x],v[a[i][0]]+=v[x],dfs(a[i][0]);
    r[x]=sum;
    deep[x]--;
}
void build(int l,int r,int s){
    if (l==r){
        f[s]=v[bz1[l]];
        return;
    }
    build(l,(l+r)/2,s+s),build((l+r)/2+1,r,s+s+1);
}
void pre(){
    static int x,y;
    for (int i=1;i<=m;i++)
        if (q[i][0]==1&&get(x=q[i][1])!=get(y=q[i][2]))
            ins(x,y),ins(y,x),b[b[x]]=b[y];
    for (int i=1;i<=n;i++){
        b[i]=i;
        if (!bz[i])deep[i]=1,dfs(i);
    }
    build(1,n,1);
    for (int i=1;i<=n;i++)v[i]=u[i];
}
void change(int l,int r,int s,int ll,int rr,int v){
    if (l>rr||ll>r)return;
    if (ll<=l&&r<=rr){
        f[s]+=v;
        return;
    }
    change(l,(l+r)/2,s+s,ll,rr,v),change((l+r)/2+1,r,s+s+1,ll,rr,v);
}
void up(int x,int y,int v){
    static int s;
    x=get(x),y=get(y);
    s=0;
    while (x!=y){
        if (deep[x]<deep[y])swap(x,y);
        change(1,n,1,bz[x],r[x],-u[x]);
        s+=u[x];
        u[x]=0;
        b[get(x)]=v;
        x=get(fa[x][0]);
    }
    change(1,n,1,bz[x],r[x],s);
    u[x]+=s;
}
int getlca(int x,int y){
    static int i;
    if (deep[x]<deep[y])swap(x,y);
    i=17;
    while (deep[x]!=deep[y]){
        for (;deep[fa[x][i]]<deep[y];i--);
        x=fa[x][i];
    }
    i=17;
    while (x!=y){
        for (;fa[x][i]==fa[y][i]&&i;i--);
        x=fa[x][i],y=fa[y][i];
    }
    return x;
}
void down(int l,int r,int s){
    if (l!=r){
        f[s+s]+=f[s],f[s+s+1]+=f[s];
        f[s]=0;
    }
}
int find(int l,int r,int s,int ll){
    down(l,r,s);
    if (l==r)return f[s];
    static int ss;
    return (ss=(l+r)/2)>=ll?find(l,ss,s+s,ll):find(ss+1,r,s+s+1,ll);
}
void work(){
    static int x,y,lca;
    for (int i=1;i<=m;i++){
        x=q[i][1],y=q[i][2];
        if (q[i][0]==1){
            if (geth(x)!=geth(y))h[h[x]]=h[y];
            else
                if (get(x)!=get(y))
                    lca=getlca(x,y),up(x,y,get(lca));
        }else
            if (q[i][0]==2){
                change(1,n,1,bz[get(x)],r[get(x)],y-v[x]);
                u[b[x]]+=y-v[x],v[x]=y;
            }else
                if (geth(x)!=geth(y))printf("-1\n");
                else{
                    lca=get(getlca(x,y));
                    x=get(x),y=get(y);
                    printf("%d\n",find(1,n,1,bz[x])+find(1,n,1,bz[y])-2*find(1,n,1,bz[lca])+u[lca]);
                }
    }
}
int main(){
    init();
    pre();
    work();
    return 0;
}

第三题 阳光
题目大意:
给定一个圆(圆心在原点上)和一个多边形(可能为凹,点按逆时针给出),求一个n边形(等边形)方案,使得n个点在圆上,且在多边形内的点最多(边上也算),输出n边形的点坐标
对于每个测试点,我们会先检测你输出的方案是否合法。合法的方案需满足以下两点:
评测方法
  1、每个刷卡点到原点的距离与r的相对误差不超过1e-10。

  2、相邻两个刷卡点的距离与a的相对误差不超过1e-9。其中,a为半径为r的圆的内接正m边形的边长。

  若你的方案合法,则直接统计你的方案中在老师视野范围内的刷卡机个数,记作yourans。我们设置了10个评分参数a1<=a2<=…<=a10,若yourans>=ai,你的得分为i。(同时满足多个取最高分)

  特别地,若你的方案不合法,则我们会选取你输出文件中第一行描述的点,记作P。随后在OP射线上选取一个点Q,使得|OQ|=r。接下来以原点为中心对Q进行旋转,从而生成一个合法的方案当作你的方案按上述规则进行评分,但你的得分会因此减少2分(扣至0为止,不会出现负分)。

我靠随机化起点,然后等角度旋转得了34分

这题在精度上异常坑。。。
改了一个上午才打完。。。

首先我们可以暴力求教(推一下多项式就可以了)

然后将圆与多边形想交的所有圆弧取出来,看一下若点分别在圆弧两端时对起点(起点在1/n圆上,可以肯定,起点范围一定在0~1/n圆上)的贡献,将他们旋转a*(1/n)圆(直到弧度在0~1/n*2pi,当然这题由于误差有点大,我将范围调大了100000倍,就是根本没限定起点范围。。,时间上还是很充裕,而且由于spf非官方版,所以到别的oj上不一定对,不贴代码)后对应到起点的弧度区间[l,r],那么说明起点在这个范围内时,在多边形内的点数+1,这样我们可以离线这些区间,然后扫一遍,记录下最大的一个区间对应弧度即可,
最后再旋转n-1次。
理论时间复杂度为nlogn
实际由于没有限定范围,最后发现离线的区间其实不是很多。。所以还是可以过的
最慢一个点为520ms
细节还需留意。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值