noip模拟2

A 四舍五入

据说是签,但是我没签出来。

观察到,题目要求的东西就是:

\[\sum_{i=1}^n \sum_{j=1}^n[ \{\frac i j \}< \frac 1 2] \]

我们这样思考:

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

D 逆序图

posted @ 2024-10-31 17:31  ccjjxx  阅读(10)  评论(0编辑  收藏  举报