模拟赛

T1

这题的思路还不错
一开始只会一个操作的做法啊
其实后来发现,两个操作本质上是一样的
但是还是不是特别好想的吧
&和|和两个标记的lazy标记要记好,考场推还是比较麻烦的

CODE:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int S=(1<<20)-1;
const int N=200005;
struct qq
{
    int l,r;
    int s1,s2;
    int mx;
    int lzy,lzy1;//区间与和区间或的标记
    int c,c1;//这里有哪些一样的都是1   这里有哪些位一样的都是0 
}tr[N*2];int num=0;
int n,q;
int read ()
{
    char ch=getchar();int x=0;
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return x;
}
void update (int now)
{
    int s1=tr[now].s1,s2=tr[now].s2;
    tr[now].mx=max(tr[s1].mx,tr[s2].mx);
    tr[now].c=(tr[s1].c&tr[s2].c);
    tr[now].c1=(tr[s1].c1|tr[s2].c1);
}
void work_and (int now,int x)
{
    tr[now].lzy&=x;tr[now].lzy1&=x;
    tr[now].mx&=x;tr[now].c&=x;tr[now].c1&=x;
}
void work_or (int now,int x)
{
    tr[now].lzy1|=x;
    tr[now].mx|=x;tr[now].c|=x;tr[now].c1|=x;
}
void push_down (int now)//人为得使得先下传and标记,再下传or标记 
{
    int s1=tr[now].s1,s2=tr[now].s2;
    if (tr[now].lzy!=S)
    {
        work_and(s1,tr[now].lzy);
        work_and(s2,tr[now].lzy);
        tr[now].lzy=S;
    }
    if (tr[now].lzy1!=0)
    {
        work_or(s1,tr[now].lzy1);
        work_or(s2,tr[now].lzy1);
        tr[now].lzy1=0;
    }
}
void bt (int l,int r)
{
    int a=++num;
    tr[a].l=l;tr[a].r=r;tr[a].lzy=S;tr[a].lzy1=0;
    if (l==r)   {tr[a].mx=tr[a].c=tr[a].c1=read();return ;}
    int mid=(l+r)>>1;
    tr[a].s1=num+1;bt(l,mid);
    tr[a].s2=num+1;bt(mid+1,r);
    update(a);
}
int query (int now,int l,int r)
{
    if (tr[now].l==l&&tr[now].r==r) return tr[now].mx;
    push_down(now);
    int s1=tr[now].s1,s2=tr[now].s2;
    int mid=(tr[now].l+tr[now].r)>>1;
    if (r<=mid) return query(s1,l,r);
    else if (l>mid) return query(s2,l,r);
    else return max(query(s1,l,mid),query(s2,mid+1,r));
}
void change (int now,int l,int r,int op,int x)
{
/*  printf("%d %d %d %d\n",l,r,tr[now].l,tr[now].r);
    system("pause");*/
    if (l==tr[now].l&&r==tr[now].r)
    {
        if (op==1)//如果这个是与操作 
        {
            int tt=(x^S);
            //他现在要吧tt里面为1的位都变成0 
            if ((tt&((tr[now].c1^S)|tr[now].c))==tt)//如果他要改的位一开始都是1,那么就一起变成0吧
            {
                work_and(now,x);
                return ;
            }
        }
        else//这是一个或操作 
        {
            //那么他现在要吧x里面为1的位都变成1
            if ((x&(tr[now].c|(tr[now].c1^S)))==x)//如果他要改的位一开始都是0,那么就一起变
            {
                work_or(now,x); 
                return ;
            }
        }
    }
    push_down(now);
    int s1=tr[now].s1,s2=tr[now].s2;
    int mid=(tr[now].l+tr[now].r)>>1;
    if (r<=mid) change(s1,l,r,op,x);
    else if (l>mid) change(s2,l,r,op,x);
    else change(s1,l,mid,op,x),change(s2,mid+1,r,op,x);
    update(now);
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
n=read();q=read();
    bt(1,n);
    for (int u=1;u<=q;u++)
    {
        int op,l,r;
        op=read();l=read();r=read();
        if (op==3) printf("%d\n",query(1,l,r));
        else 
        {
            int x=read();
            change(1,l,r,op,x);
        }
    }
    return 0;
}

T2

这题的话也是挺好的一个题,当时结论没有猜到
一定能看到圆心还是比较难看出来的吧。。
然后如果有了这个的话, O(n4) O ( n 4 ) 就十分显然了
仔细思考一个,就可以得到一个O(n^2logn)的做法了
最后的转化也比较巧妙
但是各种边界问题有点迷,最后膜标称才过的
CODE:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
const LL N=100005;

LL mu[1000005];
LL pri[1000005],tot;
bool ok[1000005];
void get_mu ()
{
    tot=0;
    memset(ok,true,sizeof(ok));
    mu[1]=1;
    for (LL u=2;u<=1000000;u++)
    {
        if (ok[u]) {pri[++tot]=u;mu[u]=-1;}
        for (LL i=1;i<=tot;i++)
        {
            LL j=pri[i];
            if (u*j>1000000) break;
            ok[u*j]=false;
            if (u%j==0) {mu[u*j]=0;break;}
            else mu[u*j]=mu[u]*mu[j];
        }
    }
//  for (int u=-)
}
LL calc (LL r,LL n)
{
//  printf("%I64d %I64d\n",r,n);
    LL lalal=0;
    LL y=min((LL)sqrt(r),n);
    for (LL x=1;x*x<r&&x<=n;x++)
    {
        while (y>0&&y*y+x*x>r) y--;
        lalal=lalal+y;
    }
    return lalal;
}
int main()
{
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    get_mu();
    LL n,r;
    LL ans=0;
    scanf("%I64d%I64d",&n,&r);n--;
    LL lim=999999999999LL/r/r;
    for (LL d=1;d*d<=lim;d++)   
    {
    //  printf("%I64d\n",d);
    ans=ans+mu[d]*calc(lim/d/d,n/d);
    }
    printf("%I64d\n",ans+2);
    return 0;
}

T3

这是我觉得这场比赛最秒的一个题了
首先,在树上有多少个联通块等同于点数减去两边都是这个点的边数,这个结论十分显然,但却不容易想到运用
得到了这个以后,由于点数是一定的,于是我们可以使得边数尽可能地小/大
然后最后对与节点度数权值的分配也很妙啊

CODE:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=100005;
int n;
int d[N];//这个点的度数 
int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    scanf("%d",&n);
    for (int u=1;u<n;u++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        d[x]++;d[y]++;
    }
    //ans=(na-ea)-(nb-eb)=(na-nb)+(eb-ea)
    sort(d+1,d+1+n);//从小到大排序
    int ans=0;
    for (int u=1;u<=n;u++)
    {
        if (u&1)
            ans=ans-d[u];
        else
            ans=ans+d[u];
    }
    ans=ans/2;
    printf("%d\n",ans+(n&1));
    return 0;   
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值