[考试总结] Tham de 基本功恢复训练系列
1.12
fAKe基本功恢复训练
(来自未来的我:反正比1.15的题目要难一些)
T1 甲苯先生的滚榜
平衡树板子题,维护两个元素
平衡树板子链接 (注释很详细)
贴个代码(Splay):
#include <bits/stdc++.h>
#define N (1000000+5)
#define DEBUG puts("!!!");
using namespace std;
typedef unsigned int ui;
ui randNum(ui &seed, ui last,const ui m){
seed=seed*17+last;
return seed%m+1;
}
ui m,sd,lst=7;
int t,n;
int rt,tot,fa[N],cnt[N],ch[N][2],vala[N],valb[N],siz[N];
int nowa[N],nowb[N];
inline void maintain(int x){
siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+cnt[x];
}
inline bool get(int x){
return x==ch[fa[x]][1];
}
inline void clear(int x){
ch[x][0]=ch[x][1]=fa[x]=cnt[x]=vala[x]=valb[x]=siz[x]=0;
}
inline void rotate(int x){
int y=fa[x],z=fa[y],k=get(x);
ch[y][k]=ch[x][k^1];
fa[ch[x][k^1]]=y;
ch[x][k^1]=y;
fa[y]=x;
fa[x]=z;
if(z) ch[z][y==ch[z][1]]=x;
maintain(y),maintain(x);
}
inline void splay(int x,int g=0){
while(fa[x]!=g){
int f=fa[x],ff=fa[f];
if(ff!=g) get(x)==get(f)?rotate(f):rotate(x);
rotate(x);
}
if(!g) rt=x;
}
inline void insert(int a,int b){
if(!rt){
vala[++tot]=a,valb[tot]=b;
rt=tot,cnt[tot]++;
maintain(rt);
return;
}
int cnr=rt,f=0;
while(1){
if(vala[cnr]==a&&valb[cnr]==b){
cnt[cnr]++;
maintain(cnr);
splay(cnr);
break;
}
f=cnr;
if(a<vala[cnr]||(a==vala[cnr]&&b>valb[cnr])) cnr=ch[cnr][1];
else cnr=ch[cnr][0];
if(!cnr){
vala[++tot]=a,valb[tot]=b;
cnt[tot]++;
fa[tot]=f,ch[f][a<vala[f]||(a==vala[f]&&b>valb[f])]=tot;
maintain(f),maintain(tot);
了 splay(tot);
break;
}
}
}
inline int rk(int a,int b){
int res=0,cnr=rt;
while(1){
if(a>vala[cnr]||(a==vala[cnr]&&b<valb[cnr])) cnr=ch[cnr][0];
else{
res+=siz[ch[cnr][0]];
if(a==vala[cnr]&&b==valb[cnr]){
splay(cnr);
return res+1;
}
res+=cnt[cnr];
cnr=ch[cnr][1];
}
}
}
inline int pre(){
int cnr=ch[rt][0];
while(ch[cnr][1]) cnr=ch[cnr][1];
return cnr;
}
inline void del(int a,int b){
rk(a,b);
if(cnt[rt]>1){
cnt[rt]--,maintain(rt);
return;
}
if(!ch[rt][0]&&!ch[rt][1]){
clear(rt),rt=0;
return;
}
if(!ch[rt][0]){
int cnr=rt;
rt=ch[cnr][1];
fa[rt]=0;
clear(cnr);
return;
}
if(!ch[rt][1]){
int cnr=rt;
rt=ch[cnr][0];
fa[rt]=0;
clear(cnr);
return;
}
int x=pre(),cnr=rt;
splay(x);
fa[ch[cnr][1]]=x;
ch[x][1]=ch[cnr][1];
clear(cnr);
maintain(rt);
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&m,&n,&sd);
memset(nowa,0,sizeof(nowa));//过于 chou lou
memset(nowb,0,sizeof(nowb));
memset(fa,0,sizeof(fa));
memset(ch,0,sizeof(ch));
memset(cnt,0,sizeof(cnt));
memset(vala,0,sizeof(vala));
memset(valb,0,sizeof(valb));
memset(siz,0,sizeof(siz));
rt=0,tot=0;
while(n--){
ui ria=randNum(sd,lst,m);
ui rib=randNum(sd,lst,m);
if(nowa[ria]) del(nowa[ria],nowb[ria]);
nowa[ria]++,nowb[ria]+=rib;
insert(nowa[ria],nowb[ria]);
lst=rk(nowa[ria],nowb[ria])-1;
printf("%d\n",lst);
}
}
return 0;
}
T2 排列计数
emmm 没写出来 我太菜了
主要是组合数的推导,详情请看洛谷的题解,有空我再补。。。
T3 余数之和
整除分块模板题
\(ans = \sum\limits_{i=1}^nk\bmod i\)
又∵ \(a\) \(\bmod\) \(b=a-b\times\lfloor{\frac{a}{b}}\rfloor\)
∴\(ans = \sum\limits_{i=1}^{n}{k-i \times \lfloor {\frac{k}{i}} \rfloor} = n \times k - \sum\limits_{i=1}^{n}{i \times \lfloor {\frac{k}{i}} \rfloor}\)
然后整除分块运算\(\lfloor {\frac{k}{i}} \rfloor\)
贴个代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n,k,ans;
int main(){
scanf("%lld%lld",&n,&k);
ans=n*k;
for(LL l=1,r=0;l<=n;l=r+1){
LL t=k/l;
if(t){
r=min(k/t,n);
}
else r=n;
ans-=t*(r-l+1)*(l+r)>>1;
}
printf("%lld",ans);
return 0;
}
1.15
真~基本功恢复训练
今天的题目挺水的,全部都是板子题
T1 小鸟的点心
(SBT) 最短路板子题,使用Dijkstra,高度超过限制的点不加入队列中
果然是基本功恢复训练,搞了两个月的文化,我连Dijkstra都不记得写了 (估计我是第一个把Dijkstra里面的堆开成大根堆的)
贴个代码:
#include <bits/stdc++.h>
#define N (200000+5)
#define int long long
using namespace std;
typedef pair<int,int> paii;
int n,m,s,t,g,x;
int h[N],lim[N],dis[N],vis[N];
vector<intnanyixie> edge[N],w[N];
void add(int u,int v,int wei){
edge[u].push_back(v);
w[u].push_back(wei);
edge[v].push_back(u);
w[v].push_back(wei);
}
void dijkstra(){
priority_queue <paii,vector<paii>,greater<paii> > q;//呜呜呜~~~
dis[s]=0;
int u=s;
q.push(make_pair(0,s));
while(!q.empty()){
u=q.top().second;q.pop();
vis[u]=1;
for(int i=0;i<edge[u].size();i++){
int now=edge[u][i];
if(dis[now]>dis[u]+w[u][i]){
dis[now]=dis[u]+w[u][i];
if(!vis[now]&&(dis[now]*x+h[now]<=lim[now])){//高度超限的不加入堆
q.push(make_pair(dis[now],now));
}
}
}
}
}
signed main(){
freopen("oyatsu.in","r",stdin);//留着防抄
freopen("oyatsu.out","w",stdout);
scanf("%lld%lld%lld%lld%lld%lld",&n,&m,&s,&t,&g,&x);
for(int i=1;i<=n;i++){
scanf("%lld%lld",&h[i],&lim[i]);
}
memset(dis,0x3f,sizeof(dis));
for(int i=1;i<=m;i++){
int u,v,wei;
scanf("%lld%lld%lld",&u,&v,&wei);
add(u,v,wei);
}
dijkstra();
if(dis[t]<=g) printf("%lld",dis[t]);
else puts("wtnap wa kotori no oyatsu desu!");
return 0;
}
T2 教主的魔法
分块板子题,昨天才写了的
上代码,不解释了,要看解释的点上面的链接
#include <bits/stdc++.h>
#define N (1000000+5)
using namespace std;
int n,q,k,bl,a[N],pos[N],lazy[N],L[N],R[N];
vector <int> v[N];
void Update(int x){
v[x].clear();
for(int i=L[x];i<=R[x];i++){
v[x].push_back(a[i]);
}
sort(v[x].begin(),v[x].end());
}
void add(int l,int r,int c){
for(int i=l;i<=min(R[pos[l]],r);i++){
a[i]+=c;
}
Update(pos[l]);
if(pos[l]!=pos[r]){
for(int i=L[pos[r]];i<=r;i++){
a[i]+=c;
}
Update(pos[r]);
}
for(int i=pos[l]+1;i<pos[r];i++) lazy[i]+=c;
}
int query(int l,int r,int c){
int x=0;
for(int i=l;i<=min(R[pos[l]],r);i++){
int sum=lazy[pos[l]]+a[i];
if(sum<c) x++;
}
if(pos[l]!=pos[r]){
for(int i=L[pos[r]];i<=r;i++){
int sum=lazy[pos[l]]+a[i];
if(sum<c) x++;
}
}
for(int i=pos[l]+1;i<pos[r];i++){
int t=c-lazy[i];
x+=lower_bound(v[i].begin(),v[i].end(),t)-v[i].begin();
}
return x;
}
int main(){
freopen("magic.in","r",stdin);//防抄
freopen("magic.out","w",stdout);
scanf("%d%d",&n,&q);
k=sqrt(n),bl=n/k+(n%k!=0);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
pos[i]=(i-1)/k+1;
v[pos[i]].push_back(a[i]);
}
for(int i=1;i<=bl;i++){
L[i]=(i-1)*k+1,R[i]=i*k;
sort(v[i].begin(),v[i].end());
}
R[bl]=n;
for(int i=1;i<=q;i++){
char opt;
int l,r,c;
scanf("%s",&opt);
scanf("%d%d%d",&l,&r,&c);
if(opt=='M') add(l,r,c);
else if(opt=='A') printf("%d\n",r-l+1-query(l,r,c));
}
return 0;
}
T3 求和
LCA板子题,先预处理每一个数的k次方,加个前缀和保证\(O(1)\)询问
其他的就不难了
估计我又是第一个把\(log_2 n\)写成\(log n\)的数学“带师”
果然我的基本功还不够扎实,不过看起来比可爱的Enterprise小姐姐好,反正都是爆零嘛~
贴一个代码,不解释了
#include <bits/stdc++.h>
#define N (300000+50)
using namespace std;
typedef long long LL;
const int mod=998244353;
int n,m,rt=1;
LL f[N][55],sum[55][N],ans;
int depth[N],fa[N][30],lgn;
vector<int> edge[N];
void add(int u,int v){
edge[u].push_back(v);
edge[v].push_back(u);
}
void dfs(int u,int father){
for(int i=0;i<edge[u].size();i++){
int now=edge[u][i];
if(now==father) continue;
else depth[now]=depth[u]+1,fa[now][0]=u;
dfs(now,u);
}
}
void getfather(){
for(int j=1;j<=lgn;j++){
for(int i=1;i<=n;i++){
fa[i][j]=fa[fa[i][j-1]][j-1];
}
}
}
int LCA(int u,int v){
if(depth[u]<depth[v]) swap(u,v);
int dist=depth[u]-depth[v];
for(int j=0;j<=lgn;j++){
if((1<<j)&dist) u=fa[u][j];
}
if(u==v) return u;
for(int j=lgn;j>=0;j--){
if(fa[u][j]!=fa[v][j]){
u=fa[u][j];
v=fa[v][j];
}
}
return fa[u][0];
}
void init(){
for(int i=1;i<=300005;i++){
for(int j=0;j<=50;j++){
if(j==0) f[i][j]=1;
else f[i][j]=f[i][j-1]%mod*i%mod;
}
}
for(int i=1;i<=50;i++){
for(int j=0;j<=300005;j++){
sum[i][j]=(sum[i][j-1]+f[j][i])%mod;
}
}
}
int main(){
freopen("sum.in","r",stdin);
freopen("sum.out","w",stdout);
init();
scanf("%d",&n);
lgn=log2(n);
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
}
dfs(rt,0);
getfather();
scanf("%d",&m);
for(int i=1;i<=m;i++){
int u,v,k;
scanf("%d%d%d",&u,&v,&k);
int lca=LCA(u,v);
int du=depth[u],dv=depth[v],dl=depth[lca];
if(lca==u){
printf("%lld\n",(sum[k][dv]-sum[k][du-1]+mod)%mod);
}
else if(lca==v){
printf("%lld\n",(sum[k][du]-sum[k][dv-1]+mod)%mod);
}
else printf("%lld\n",((sum[k][du]-sum[k][dl-1]+mod)%mod+(sum[k][dv]-sum[k][dl-1]+mod)%mod-f[dl][k]+mod)%mod);
}
return 0;
}
总结
看来还要多多训练才行啊
转载请注明出处--Xx_queue