【被虐】第三场选拔赛补题

补题时间。。。

这回出题人出的都是mbg风格的题。。。话不多说,挑几个有意思的说说。

B题,题意给定\(n\leqslant 10^9\),求

\[\sum_{p\leqslant n}\frac 1p,\ (p\ is\ prime) \]

的值并四舍五入取整。

显然直接求是不行的,因为会超时,事实上这题我目前还没发现有什么低于线性的算法,所以这题差不多是个结论题。结论是,在\(10^9\)的范围内,该和式的值取整后不会超过3,所以只要二分找出几个值的边界即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=6e6+5;
int prime[N],cnt;
bool vis[N];
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int n;
		scanf("%d",&n);
		if(n<3) printf("0\n");
		else if(n<29) printf("1\n");
		else if(n<11789) printf("2\n");
		else printf("3\n");
	}
	return 0;
}

F题:

image-20201124205304429

一眼看上去很牛逼,其实很沙比。。。就模拟就行了。。。

但是这题正好触及到了我的知识盲区(动态空间数组),只要会vector就行了(或手写链表)。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
#define i8 __int128
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define vrep(i,__v) for(int i=0;i<__v.size();i++)
inline i8 read()
{
    i8 ret=0,sgn=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {if(c=='-') sgn=-1;c=getchar();}
    while(c>='0'&&c<='9')
    {ret=ret*10+c-'0';c=getchar();}
    return ret*sgn;
}
void write(i8 x)
{
    if(x<0) x=-x,putchar('-');
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
const int N=1e6+5;
vector<int> v[N];
int main()
{
   int n,x;
   scanf("%d",&n);
   rep(i,1,n)
   {
       scanf("%d",&x);
       v[x].push_back(i);
   }
   int ans=2e9;
   rep(i,1,n)
   {
       scanf("%d",&x);
       vrep(j,v[x])
       {
           ans=min(ans,abs(v[x][j]-i));
       }
   }
   if(ans==2e9) printf("-1\n");
   else printf("%d\n",ans);
   return 0;
}

H题:给一颗有根树,树节点有权值,有两种操作:1.单点修改,将一个节点的权值+v。2.查询,求某节点到树根的简单路径上所有点权平方和,答案对\(2^{64}\)取模。

这个题考试的时候想到了树链剖分 ,但是死活调不出来,自己以为是树链剖分写次了,结果没想到是一个前所未见的玄学错误。。。我对模数赋值的时候直接赋值成了\(2^{64}\)。。。然后成功RE。。。(事实上这个数在计算机里根本不存在。。。),这也侧面证明了树剖没错。。。哭了,然而自己赛后纠错后还是T了(树剖复杂度真高)。事实上,存在一种很简单的算法,仅仅需要搞出树的dfs序即可。我们维护一个类似前缀和的东西,维护每个点到根节点路径的答案,在dfs的时候可以直接求出来,考虑一次单点修改只对某节点及其子树的答案有影响,我们再维护一个树状数组,差分修改,然后求的时候直接查询树状数组就好了。(其实很简单)

(小技巧:取模\(2^{64}\)可以用unsigned long long存储数据,自然溢出就可以了。然而我用的int128也不是不可以。。。)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;//simplify long long
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
#define inf 0x3f3f3f3f
#define pi 3.14159265358979
#define rep(i, l, r) for(int i = l; i <= r; i ++)
#define lop(i, r, l) for(int i = r; i >= l; i --)
#define step(i, l, r, __step) for(int i = l; i <= r; i += __step)
#define revp(i, r, l, __step) for(int i = r; i >= l; i -= __step)
#define reg regsiter
#define RI regsiter int
#define RL regsiter long long
#define rerep(i, l, r) for(regsiter int i = l; i <= r; i ++)
#define relop(i, r, l) for(regsiter int i = r; i >= l; i --)
#define i8 __int128
//#define __int128 ll//don't forget delete it in contest!
inline i8 read()//fast read
{
	i8 ret = 0, sgn = 1;
	char chr = getchar();
	while(chr < '0' || chr > '9')
	{if(chr == '-') sgn = -1; chr = getchar();}
	while(chr >= '0' && chr <= '9')
	{ret = ret * 10 + chr - '0'; chr = getchar();}
	return ret * sgn;
}
void write(i8 x)//int128's output
{
	if(x < 0) putchar('-'), x = -x;
	if(x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
const int N = 1e6 + 5;
const int M = 2e5 + 5;
int n,m,S;
struct edge{
	int to,nxt;
}e[N<<1];
int head[N],tot;
void adde(int f,int t)
{
	e[++tot]=(edge){t,head[f]};
	head[f]=tot;
}
int deep[N],fa[N],siz[N];
int inseg[N],intr[N],scnt=0;
i8 sum[N],num[N],mod=(1ll<<63);
void dfs(int u,int f)
{
	deep[u]=deep[f]+1;
	sum[u]=sum[f]+num[u]*num[u];
	fa[u]=f;
	siz[u]=1;
	inseg[u]=++scnt;
	intr[scnt]=u;
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==f) continue;
		dfs(v,u);
		siz[u]+=siz[v];
	}
}
i8 atr[N];
#define lowbit(x) x&-x
void modify(int x,i8 v)
{
	while(x<=n)
	{
		atr[x]+=v,atr[x]%=mod;
		x+=lowbit(x);
	}
}
i8 ask(int x)
{
	i8 ret=0;
	while(x)
	{
		ret+=atr[x],ret%=mod;
		x-=lowbit(x);
	}
	return ret;
}
int main()
{
	mod*=2;
	scanf("%d%d%d",&n,&m,&S);
	rep(i,1,n) num[i]=read();
	int a,b;
	rep(i,1,n-1)
	{
		scanf("%d%d",&a,&b);
		adde(a,b);
		adde(b,a);
	}
	dfs(S,0);
	i8 v,w;
	while(m--)
	{
		scanf("%d%d",&a,&b);
		if(a==1)
		{
			v=read();
			w=2ll*v*num[b]+v*v;
			modify(inseg[b],w);
			modify(inseg[b]+siz[b],-w);
			num[b]+=v;
		}
		else
		{
			w=ask(inseg[b]);
			write((w+sum[b])%mod);
			putchar('\n');
		}
	}
	return 0;
}

I题:题目很短,求

\[\sum_{i=l}^rC_i^k \]

\[k\leqslant200,l,r\leqslant 10^9 \]

其实是个神仙题。。。把式子拆开看:

\[C_l^k+C_{l+1}^k+\cdots+C_r^k \]

这个式子看不出什么来,对这个式子进行一次转化,由

\[C_n^m=C_n^{n-m} \]

\[C_l^{l-k}+C_{l+1}^{l-k+1}+\cdots+C_r^{r-k} \]

\[=C_l^{l-k}+C_l^{l-k-1}+C_{l+1}^{l-k+1}+\cdots+C_r^{r-k}-C_l^{l-k-1} \]

前两项可以由杨辉三角公式合并,此时变为

\[C_{l+1}^{l-k}+C_{l+1}^{l-k+1}+C_{l+2}^{l-k+2}+\cdots+C_r^{r-k}-C_l^{l-k-1} \]

此时新的两项又可以同样的方式合并,一直合并到最后,则原式等于

\[C_{r+1}^{r-k}-C_l^{l-k-1} \]

虽然已经化到最简形式了,但是此时还是无法卢卡斯求。我们注意到k很小,并对组合数阶乘展开,发现最后只需要计算小于200项的连乘和k阶乘的逆元,于是连乘暴力求就完事了,阶乘可以暴力也可以先预处理。

代码很简单

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define i8 __int128
#define __int128 ll
#define rep(i,l,r) for(int i=l;i<=r;i++)
ll mod=1e9+7;
ll f[1005];
void init()
{
	f[0]=1;
	rep(i,1,988) f[i]=1ll*i*f[i-1],f[i]%=mod;
}
ll ksm(ll x,ll y)
{
	ll ret=1;
	while(y)
	{
		if(y&1) ret*=x,ret%=mod;
		x*=x,x%=mod;
		y>>=1;
	}
	return ret;
}
ll inv(ll x)
{
	return ksm(x,mod-2);
}
int main()
{
	int T;
	scanf("%d",&T);
	init();
	while(T--)
	{
		ll l,r,k;
		scanf("%lld%lld%lld",&l,&r,&k);
		ll ans1=1;
		rep(i,max(r-k+1,0ll),r+1) ans1*=1ll*i,ans1%=mod;
		ans1*=inv(f[k+1]),ans1%=mod;
		ll ans2=1;
		rep(i,max(0ll,l-k),l) ans2*=1ll*i,ans2%=mod;
		ans2*=inv(f[k+1]),ans2%=mod;
		ll ans=ans1-ans2;
		ans=(ans%mod+mod)%mod;
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2020-11-24 21:09  Frank喵^_^  阅读(89)  评论(0编辑  收藏  举报