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;
}