补题时间。。。
这回出题人出的都是mbg风格的题。。。话不多说,挑几个有意思的说说。
B题,题意给定(nleqslant 10^9),求
的值并四舍五入取整。
显然直接求是不行的,因为会超时,事实上这题我目前还没发现有什么低于线性的算法,所以这题差不多是个结论题。结论是,在(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
");
else if(n<29) printf("1
");
else if(n<11789) printf("2
");
else printf("3
");
}
return 0;
}
F题:
一眼看上去很牛逼,其实很沙比。。。就模拟就行了。。。
但是这题正好触及到了我的知识盲区(动态空间数组),只要会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
");
else printf("%d
",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('
');
}
}
return 0;
}
I题:题目很短,求
其实是个神仙题。。。把式子拆开看:
这个式子看不出什么来,对这个式子进行一次转化,由
前两项可以由杨辉三角公式合并,此时变为
此时新的两项又可以同样的方式合并,一直合并到最后,则原式等于
虽然已经化到最简形式了,但是此时还是无法卢卡斯求。我们注意到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
",ans);
}
return 0;
}