noip模拟2
A 四舍五入
据说是签,但是我没签出来。
观察到,题目要求的东西就是:
我们这样思考:
令 \(i\times j=k\),那么有 \(i\times (j+1)=k+i\)。
进一步地,\(i\times (j+\frac 1 2)=k+\frac 1 2 i\)
我们假设 \(k+\frac 1 2 i\) 是个整数,那么 \(\frac{k+\frac 1 2 i}{i}=j+\frac 1 2\)
根据不等关系,我们猜测:令 \(z\in [0,\frac 1 2)\),有 \(\frac {k+ zi}{i}<j+\frac 1 2\)。
意思是,如果 \(i\mod j<\frac 1 2 j\),那么也满足上述题目要求的关系。
这样问题就被转化了。
对于每一个 \(j\in [1,n]\) 的倍数 \(xj \in[1,n]\),区间 \([i,i+\frac{j-1}{2}]\) 的值都会有一个 \(1\) 的贡献。
每次用差分统计即可。时间复杂度 \(O(n \ln n)\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
const int N=2e6+6;
void write(int x)
{
if(x>9) write(x/10);
putchar(x%10+'0');
}
int a[N];
signed main()
{
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
scanf("%lld",&n);
for(int j=1;j<=n;j++)
{
for(int i=0;i<=n;i+=j)
{
int l=i,r=i+(j-1)/2+1;
a[l]++,a[r]--;
}
}
for(int i=1;i<=n;i++)a[i]+=a[i-1],write(a[i]),putchar(' ');
return 0;
}
B 填算符
这个题其实离正解很近了。
先说 \(a_i\) 是 \(2\) 的幂次的解法。
因为 \(a_i<2^{60}\),所以我们直接进行一个奇妙的哈希,哈希函数是 \(val_i=\log_2 a_i\)。
然后就得到了一堆小于 \(60\) 的数,方便我们操作。
观察样例给出的答案,根据答案反推过程在这里极为好用。
我们定位每一个答案选取的点位,发现前半部分全部是顺序递增的,后面的跨度很大。
那这就说明答案会有两部分组成:一部分是特殊的,另一部分是连续的、剩余的。
再次找规律,看到答案最后面的那些对应的点位前一个数都是 \(9\)。
为什么是 \(9\)?
为什么不是别的数?
我们观察到,最后一个答案后面的所有数都没有 \(9\),但是其他的数都有。
我们记录所有数最后一次出现的位置,发现 \(9\) 是最靠前的!
那这个策略就很明晰了,选取最后一次出现最早的数,让它和后面与起来,在最后保证所有能取到的二进制位都能取到。
那为什么最后一个之前还有那么多要取到 \(9\) 呢?
这是因为,你需要在最后与答案的前面留下一个 \(9\) 号位置,要不然取不到了。
那为什么前面要连续地取呢?
这是因为可能把 \(9\) 取完会比 \(k\) 少,那就需要在超出范围前把剩下没取到的位置加到答案中。
但是在答案里,\(9\) 的位置需要一直保留,因为有最优性决策问题,你每次从后往前选取 \(\&\) 的位置一定是越靠后越优秀,那只有在 \(9\) 的位置继承上一个 \(9\),抛弃其他位置才是最优。
就是这样。
点击查看代码
for(int i=1;i<=n;i++) a[i]=__lg(a[i]),pre[i]=t[a[i]],t[a[i]]=i,++cnt[a[i]];
int mi=1e9,pos=0;
for(int i=0;i<=60;i++)
if(t[i]) if(mi>t[i]&&t[i]>k+1) mi=t[i],pos=i;
int p=0;
for(int i=n;i>=1;i--)
{
if(a[i]==pos)
{
if(e.size()+pre[i]<=k){p=i;break;}
e.push_back(i-1);
}
}
for(int i=p-1;e.size()<k;i--)
e.push_back(i-1);
sort(e.begin(),e.end());
for(int v:e) cout<<v<<" ";
再看正解。
首先,最终的最大答案一定是取前 \(k\) 个空放 \(\&\) 后面的全放 \(|\)。
然后从高往低,对于每一位,和上面思路一样去判断最后一个位置后的答案是否是最优。
只要找到一个,那么就可能从后往前找有这一位的数,直接加入答案,向前更新即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k;
const int N=1e6+6;
int a[N],s[N],l[N];
vector<int>e;
int ans;
signed main()
{
freopen("bitop.in","r",stdin);
freopen("bitop.out","w",stdout);
cin>>n>>k;int mx=0,mw=0;
for(int i=1;i<=n;i++) cin>>a[i],mx=max(a[i],mx);
for(int i=59;i>=0;--i) if(mx&(1<<i))mw=1<<i;
ans=a[1];
for(int i=1;i<=k+1;++i) ans&=a[i];
for(int i=k+2;i<=n;++i) ans|=a[i-1];
for(int i=n;i;--i)s[i]=s[i+1]|a[i];
int len=n;
int mn=1ll<<59;
for(int i=59;i>=0;--i)//要取到这个位置的答案,就要与起来这个位置最后一个数
{
int x=1ll<<i,la=0;
if(ans&x)
{
for(int j=1;j<=len;++j)
if(a[j]&x)l[j]=la,la=j;//l:上一个有这个位置的数的id
len=la;mn=min(mn,x);
if(s[len+1]==ans)break;// 从 lst+1到n的位置满足答案,那这前面的都取。
else
{
for(int j=1;j<=n;++j)
a[j]-=s[len+1]&a[j];
ans-=s[len+1];
for(int j=n;j;--j)s[j]=s[j+1]|a[j];
}
}
}
int now=len;
for(int i=l[len],tmp=0;i;i=l[i])//按照最大的,最后的数开始取,和幂次点策略一致
{
++tmp;
if(tmp==k)
{
for(int i=len;i!=now;i=l[i])e.push_back(i-1),--k;
e.push_back(--now);
sort(e.begin(),e.end());
for(int v:e) cout<<v<<" ";
exit(0);
}
if(i-2>=k-tmp)now=i;
else break;
}
for(int i=len;i!=now;i=l[i])e.push_back(i-1),--k;
--now;
while(k--)e.push_back(--now);
sort(e.begin(),e.end());
for(int v:e) cout<<v<<" ";
}
C 道路修建
太魔怔了这道题。
无法改。
路径长度和可转化为边经过的次数。
对于一条边,分成两类:环上的边和子树内的边。
对于环上的边,经过次数是 \(L\displaystyle\times (n^2-\sum siz^2(x))\);
对于子树内的边,它经过的次数是原本经过次数+加边后的新增次数。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e5+5;
const int mod=998244353;
vector<int>e[N];
int n,q,tp,dep[N],siz[N],son[N],id[N],top[N],rk[N],tim,f[N],k[N],fa[N],ANS;
void dfs1(int u,int F)
{
siz[u]=1,dep[u]=dep[F]+1,fa[u]=F;
for(int v:e[u])
{
if(v==F) continue;
dfs1(v,u);siz[u]+=siz[v];
f[u]=(f[u]+f[v]+siz[v])%mod;
if(siz[v]>siz[son[u]]) son[u]=v;
}
k[u]=siz[u]*(n-siz[u])%mod;
ANS=(ANS+k[u])%mod;
}
int p[N],g[N],tot[N];
void dfs2(int u,int t)
{
top[u]=t,id[u]=++tim,rk[tim]=u;
if(son[u])
{
p[u]=(f[u]-f[son[u]]-siz[son[u]]+mod*2)%mod;
g[u]=siz[u]-siz[son[u]];
tot[son[u]]=(tot[u]+n-2*siz[son[u]]+mod*2)%mod;
dfs2(son[u],t);
}
for(int v:e[u])
{
if(v==fa[u]||v==son[u]) continue;
tot[v]=(tot[u]+n-2*siz[v]+mod*2)%mod;
dfs2(v,v);
}
}
int calc(int v)
{
return v*(v-1)/2%mod;
}
struct SegTree{
struct node{
int s1,s2,s3;
}tr[N<<2];
#define lid now<<1
#define rid now<<1|1
void pushup(int now)
{
tr[now].s1=tr[lid].s1+tr[rid].s1,tr[now].s2=tr[lid].s2+tr[rid].s2,tr[now].s3=tr[lid].s3+tr[rid].s3;
}
void build(int now,int l,int r)
{
if(l==r){int d=rk[l];tr[now].s1=k[d],tr[now].s2=p[d]*(n-g[d])%mod,tr[now].s3=calc(g[d]);return ;}
int mid=(l+r)>>1;
build(lid,l,mid),build(rid,mid+1,r),pushup(now);
}
int query(int now,int l,int r,int x,int y,int op)
{
if(x<=l&&r<=y)
{
if(op==0) return tr[now].s1;
if(op==1) return tr[now].s2;
if(op==2) return tr[now].s3;
}
int mid=(l+r)>>1,res=0;
if(x<=mid) res=(res+query(lid,l,mid,x,y,op));
if(y>mid) res=(res+query(rid,mid+1,r,x,y,op));
return res;
}
}st;
int ask(int x,int y)
{
if(x==y)return 0;
int X=x,Y=y,fx=top[x],fy=top[y],lstx=0,lsty=0,res[3]={0,0,0};
while(fx!=fy)
{
if(dep[fx]<dep[fy])swap(X,Y),swap(x,y),swap(fx,fy),swap(lstx,lsty);
res[0]=(res[0]+st.query(1,1,n,id[fx],id[x],0))%mod;
if(fx!=x)res[1]=(res[1]+st.query(1,1,n,id[fx],id[x]-1,1))%mod;
if(x==X)res[1]=(res[1]+f[x]*(n-siz[x]))%mod;
else res[1]=(res[1]+(f[x]-f[lstx]-siz[lstx]+mod*2)*(n-(siz[x]-siz[lstx]))%mod)%mod;
if(fx!=x)res[2]=(res[2]+st.query(1,1,n,id[fx],id[x]-1,2))%mod;
if(x==X)res[2]=(res[2]+calc(siz[x]))%mod;
else res[2]=(res[2]+calc(siz[x]-siz[lstx]))%mod;
lstx=fx,x=fa[fx],fx=top[x];
}
if(dep[x]<dep[y])swap(X,Y),swap(x,y),swap(lstx,lsty);
int lca=y;
if(x!=y)
{
res[0]=(res[0]+st.query(1,1,n,id[y]+1,id[x],0))%mod;
if(id[x]-id[y]+1>=3) res[1]=(res[1]+st.query(1,1,n,id[y]+1,id[x]-1,1))%mod,res[2]=(res[2]+st.query(1,1,n,id[y]+1,id[x]-1,2))%mod;
if(id[x]-id[y]+1>=2)
{
if(x!=X)res[1]=(res[1]+(f[x]-f[lstx]-siz[lstx]+mod*2)*(n-(siz[x]-siz[lstx]))%mod)%mod,res[2]=(res[2]+calc(siz[x]-siz[lstx]))%mod;
else res[1]=(res[1]+f[x]*(n-siz[x]))%mod,res[2]=(res[2]+calc(siz[x]))%mod;
}
res[1]=(res[1]+(tot[lca]-f[son[lca]]-f[lsty]-siz[son[lca]]-siz[lsty])*(siz[son[lca]]+siz[lsty]))%mod,res[2]=(res[2]+calc(n-siz[son[lca]]-siz[lsty]))%mod;
}
else
{
res[1]=(res[1]+(tot[lca]-f[lstx]-f[lsty]-siz[lstx]-siz[lsty])*(siz[lstx]+siz[lsty]))%mod,res[2]=(res[2]+calc(n-siz[lstx]-siz[lsty]))%mod;
}
int dis=dep[X]+dep[Y]-2*dep[lca]+1;
res[2]=(calc(n)-res[2]+mod)%mod;
int ans=((res[1]+dis*res[2]-res[0])%mod+mod)%mod;
return ans;
}
signed main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>q>>tp;
for(int i=1;i<n;i++)
{
int u,v;cin>>u>>v;
e[u].push_back(v),e[v].push_back(u);
}
dfs1(1,0),tot[1]=f[1],dfs2(1,1);
st.build(1,1,n);
int lstans=0;
while(q--)
{
int u,v;cin>>u>>v;
if(tp) u^=lstans,v^=lstans;
cout<<(lstans=(ask(u,v)+ANS)%mod)<<"\n";
}
return 0;
}