洛谷 P5354 [Ynoi2017]由乃的OJ
洛谷 P5354 [Ynoi2017]由乃的OJ
调了好几天,感觉题号已经刻进DNA里了……
等等这是签到题?

KONO题面哒!(只截取重点部分,完整题面请前往洛谷)
给你一个有\(n\)个点的树,每个点的包括一个位运算\(opt\)和一个权值\(x\),位运算有$&, | , $ ^ 三种,分别用\(1,2,3\)表示。
每次询问包含三个数\(x,y,z\),初始选定一个数\(v\)。然后\(v\)依次经过从\(x\)到\(y\)的所有节点,每经过一个点\(i\),\(v\)就变成\(v\) \(opt_i\) \(x_i\),所以他想问你,最后到\(y\)时,希望得到的值尽可能大,求最大值?给定的初始值\(v\)必须是在\([0,z]\)之间。每次修改包含三个数\(x,y,z\),意思是把\(x\)点的操作修改为\(y\),数值改为\(z\)
输入格式
一行三个数\(n,m,k\)。\(k\)的意义是每个点上的数,以及询问中的数值\(z\)都 \(<2^k\)。
之后\(n\)行每行两个数\(x,y\)表示该点的位运算编号以及数值
之后\(n - 1\)行,每行两个数\(x,y\)表示\(x\)和\(y\)之间有边相连
之后\(m\)行,每行四个数,\(Q,x,y,z\)表示这次操作为\(Q\)(\(1\)为询问,\(2\)为修改),\(x,y,z\)意义如题所述
数据范围
\(0 \leq n , m \leq 100000 , k \leq 64\)
不开ull见祖宗警告
题解
首先题目中提到了本体的原题叫睡觉困难综合症,有兴趣的可以先A掉。
树链剖分的操作……应该可以略过吧?关于线段树上记什么请看后文。
然后我们观察一下题目所说的每条路径上的操作,发现全都是位运算。我们知道位运算对于一个数就相当于对它的每一个二进制位进行一次对应的逻辑运算,且不同的位运算不满足交换律。
题目要求我们求出输入一个数时最大的输出,于是我们想到,可以在线段树上记每个区间上每一位输入0和输入1时会输出什么。每次查询时取出\(x\)到\(y\)路径的两个数值(语文不好,真不知道怎么描述),从高位开始向下贪心(注意不要从\(k-1\)位开始,因为上位输入0可能会更大,从\(k-1\)位开始可能会统计不到)。
贪心思路:(初值\(ans=tmp=0\))
如果第\(i\)位输入0能得1,则\(ans+=(1<<i)\),进入下一位。
如果第\(i\)位输入1能得1,在\(tmp+(1<<i) \leq z\)时\(ans+=(1<<i)\),\(tmp+=(1<<i)\),进入下一位。
大致思路就是如此。
接下来我们就要维护和取出我们记的值了。由于上文讲到的位运算的性质,我们可以把每一位想象成一个状态,然后像状态压缩那样搞:
(设左半为\(l\),右半为\(r\))(此处简述每一位的情况)(\(f0\)为全输入0,\(f1\)同理)
-
从左到右依次经过第\(i\)位得1的条件:
-
- \(l.f0[i]\)(表示\(f0\)的第\(i\)位,下文同理)\(=1\),且\(r.f1[i]=1\) :\(l\)的第\(i\)位输入0得1,且\(r\)的第\(i\)位输入1得1。
- \(l.f0[i]!=1\),且\(r.f0[i]=1\):\(l\)的第\(i\)位输入0得0,且\(r\)得第\(i\)位输入1得1。
这样一来,\(f1\)的得1的条件也就很好想了。
所以就不解释了- \(l.f1[i]=1,r.f1[i]=1;\)
- \(l.f1[i]!=1,r.f0[i]=1;\)
注意,由于题目要求依次经过,所以还要从大到小反过来记一次。
但你们可能好奇了,这不是对每一位的逻辑运算吗,复杂度升天啊。
前文讲到过,位运算实质是对每一位进行对应的逻辑运算,所以,我们可以直接用每个逻辑匀速啊对应的位运算一次性合并每一位的状态。(注意,\(!\)的对应位运算是~,我被这个坑了很久)
取数时要注意哪些是从小到大哪些是从大到下的,要分开运算。合并链的时候也要注意先后顺序,不然会又A又WA调很久。
一定要记得开ull。当你WA的很惨时,不妨试试把所有疑似的数据都开上ull,万一就A了呢?
KONO代码哒!
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef unsigned long long ll;
char *p1,*p2,buf[1<<20];
//#define GC (p1==p2&&(p1=buf,p2=buf+fread(buf,1,1<<20,stdin),p1==p2)?0:(*(p1++)))
#define GC getchar()
inline ll ll_in(){
ll x=0;
int w=0;
char ch=0;
while(!isdigit(ch)){
w|=ch=='-';
ch=GC;
}
while(isdigit(ch)){
x=(x<<3ull)+(x<<1ull)+(ll)(ch^48ull);
ch=GC;
}
return w? -x:x;
}
inline int int_in()
{
int x=0;
int w=0;
char ch=0;
while(!isdigit(ch)){
w|=ch=='-';
ch=GC;
}
while(isdigit(ch)){
x=(x<<3)+(x<<1)+(ch^48);
ch=GC;
}
return w? -x:x;
}
const int maxn=100010;
const ll inf=0-1;
struct edge{
int next,to;
}g[2*maxn];
int head[maxn],cnt;
void add(int from,int to)
{
g[++cnt].next=head[from];
g[cnt].to=to;
head[from]=cnt;
}
ll opt(ll a,int act,ll b)
{
switch(act){
case 1:{
a=(a&b);
break;
}
case 2:{
a=(a|b);
break;
}
case 3:{
a=(a^b);
break;
}
}
return a;
}
int Top[maxn],Size[maxn],id[maxn],zson[maxn];
ll val[maxn],v[maxn];
int sw[maxn],s[maxn];
int dep[maxn],f[maxn];
int num=0;
int n,m,k;
struct node{
ll f0,f1;
ll fx0,fx1;
};
struct tree{
node t[maxn<<3];//0?????,1?????
inline int ls(int x){return x<<1;}
inline int rs(int x){return x<<1|1;}
node update(node l,node r){
node res;
res.f0=((l.f0&r.f1)|((~l.f0)&r.f0));
res.f1=((l.f1&r.f1)|((~l.f1)&r.f0));
res.fx0=((r.fx0&l.fx1)|((~r.fx0)&l.fx0));
res.fx1=((r.fx1&l.fx1)|((~r.fx1)&l.fx0));
return res;
}
void pushup(int p){
t[p]=update(t[ls(p)],t[rs(p)]);
}
void build(int p,int l,int r)
{
if(l==r){
t[p].f0=t[p].fx0=opt(0,sw[l],val[l]);
t[p].f1=t[p].fx1=opt(inf,sw[l],val[l]);
return;
}
int mid=((l+r)>>1);
build(ls(p),l,mid);
build(rs(p),mid+1,r);
pushup(p);
}
void ins(int x,int p,int l,int r,ll k,ll op)
{
if(l==r){
val[l]=k;
sw[l]=op;
t[p].f0=t[p].fx0=opt(0,sw[l],val[l]);
t[p].f1=t[p].fx1=opt(inf,sw[l],val[l]);
return;
}
int mid=((l+r)>>1);
if(x<=mid)ins(x,ls(p),l,mid,k,op);
else ins(x,rs(p),mid+1,r,k,op);
pushup(p);
}
node get_sum(int x,int y,int p,int l,int r){
if(x<=l&&y>=r){
return t[p];
}
int mid=((l+r)>>1);
node r1,r2;
if(x<=mid)r1=get_sum(x,y,ls(p),l,mid);
if(y>mid)r2=get_sum(x,y,rs(p),mid+1,r);
if(x<=mid&&y>mid){
node r;
r=update(r1,r2);
return r;
}
else{
if(x<=mid)return r1;
else return r2;
}
}
node ans1[maxn],ans2[maxn];
int cnt1,cnt2;
node q_sum(int x,int y)
{
cnt1=cnt2=0;
node ans;
while(Top[x]!=Top[y]){
if(dep[Top[x]]<dep[Top[y]]){
ans1[++cnt1]=get_sum(id[Top[y]],id[y],1,1,n);
y=f[Top[y]];
}
else{
ans2[++cnt2]=get_sum(id[Top[x]],id[x],1,1,n);
x=f[Top[x]];
}
}
if(dep[x]>dep[y]){
swap(x,y);
ans2[++cnt2]=get_sum(id[x],id[y],1,1,n);
}
else ans1[++cnt1]=get_sum(id[x],id[y],1,1,n);
node r1,r2;
r1=ans1[cnt1],r2=ans2[1];
int i;
for(i=cnt1-1;i>0;i--)r1=update(r1,ans1[i]);
for(i=2;i<=cnt2;i++)r2=update(ans2[i],r2);
if(cnt2==0)return r1;
if(cnt1==0){
swap(r2.f0,r2.fx0);
swap(r2.f1,r2.fx1);
return r2;
}
ans.f0=((r2.fx0&r1.f1)|((~r2.fx0)&r1.f0));
ans.f1=((r2.fx1&r1.f1)|((~r2.fx1)&r1.f0));
return ans;
}
}tr;
void dfs1(int x,int fr)
{
dep[x]=dep[fr]+1;
f[x]=fr;
Size[x]=1;
int _max=0;
for(int i=head[x];i;i=g[i].next)
{
int v=g[i].to;
if(v==fr)continue;
dfs1(v,x);
Size[x]+=Size[v];
if(Size[v]>_max){
zson[x]=v;
_max=Size[v];
}
}
}
void dfs2(int x,int fr,int top)
{
Top[x]=top;
id[x]=++num;
sw[num]=s[x];
val[num]=v[x];
if(!zson[x])return;
dfs2(zson[x],x,top);
for(int i=head[x];i;i=g[i].next){
int v=g[i].to;
if(v==fr||v==zson[x])continue;
dfs2(v,x,v);
}
}
int main()
{
// freopen("test.in","r",stdin);
// freopen("my.out","w",stdout);
n=int_in();m=int_in();k=int_in();
int i,j;
for(i=1;i<=n;i++)
{
s[i]=int_in();
v[i]=ll_in();
}
for(i=1;i<n;i++)
{
int a,b;
a=int_in();b=int_in();
add(a,b);
add(b,a);
}
dfs1(1,0);
dfs2(1,0,1);
tr.build(1,1,n);
ll sb=1;
while(m--){
ll q,x,y,z;
q=ll_in();x=ll_in();y=ll_in();z=ll_in();
switch(q){
case 1:{
node res=tr.q_sum(x,y);
ll ans=0,tmp=0;
for(i=63;i>=0;i--){
if((res.f0>>i)&1ull){
ans+=(1ull<<i);
continue;
}
if((res.f1>>i)&1ull){
if(tmp+(1ull<<i)<=z){
tmp+=(1ull<<i);
ans+=(1ull<<i);
continue;
}
}
}
cout<<ans<<endl;
break;
}
case 2:{
tr.ins(id[x],1,1,n,z,y);
break;
}
}
}
return 0;
}
然后提供一个数据生成器,当你绝望时,不妨试试这个东西让你更绝望
#include<iostream>
#include<cstdio>
#include<random>
#define ran(x) (rand()%x)+1
using namespace std;
typedef unsigned long long ll;
ll n,m,k;
int un[1010],cnt;
int main()
{
srand(time(0));
freopen("test.in","w",stdout);
n=ran(7);m=ran(8);k=ran(10);
printf("%d %d %d\n",n,m,k);
k=(1<<k);
un[1]=cnt=1;
int i,j;
for(i=1;i<=n;i++){
printf("%d %d\n",ran(3),ran(k));
}
for(i=1;i<n;i++){
int v=un[ran(cnt)];
un[++cnt]=i+1;
printf("%d %d\n",v,i+1);
}
for(i=1;i<=m;i++){
int opt=ran(2);
printf("%d ",opt);
if(opt==1){
printf("%d %d %d\n",ran(n),ran(n),ran(k));
}
else{
printf("%d %d %d\n",ran(n),ran(3),ran(k));
}
}
}

浙公网安备 33010602011771号