T1
带编号的斐波那契兔子,m个询问,给出两只兔子的编号,求最近公共祖先编号。
记兔子总数为 f i f_i fi,如果 f i − 1 < x ≤ f i f_{i-1}<x\le f_i fi−1<x≤fi,那么 x x x就是由 f i − 2 f_{i-2} fi−2生出来的。 x − f i − 1 x-f_{i-1} x−fi−1就是 x x x父亲的编号。斐波那契数60项就到了1012,所以直接暴力往上找父亲就好了。
#include<bits/stdc++.h>
#define LL long long
#define maxn 65
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
template<class T>inline void read(T &a){
char c;while(!isdigit(c=getc()));
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
template<class T>inline void write(T x){
if(x>=10) write(x/10);
putchar(x%10+48);
}
const int len = 60;
int m,x,y;
LL a,b,f[maxn];
int main()
{
freopen("fibonacci.in","r",stdin);
freopen("fibonacci.out","w",stdout);
f[1]=1,f[2]=2;
for(int i=3;i<=len;i++) f[i]=f[i-1]+f[i-2];
read(m);
while(m--){
read(a),read(b);
x=lower_bound(f+1,f+1+len,a)-f;
y=lower_bound(f+1,f+1+len,b)-f;
while(a!=b){
if(x<y) swap(x,y),swap(a,b);
a-=f[x-1];
while(f[x-1]>=a) x--;
}
write(a),putchar('\n');
}
}
T2
洛谷P3939 数颜色
序列,颜色
a
i
a_i
ai,两种操作:询问
[
l
,
r
]
[l,r]
[l,r]颜色为
x
x
x的个数;交换
a
x
a_x
ax和
a
x
+
1
a_{x+1}
ax+1。 n,m<=300000
一眼暴力分块时间超限。
两眼动态开点思维僵化。
题解颜色排序惊为天人。
pair<颜色,位置>排序,查询就lower_bound找到对应区间即可,相邻修改不会改变同种颜色位置的相对顺序。或者使用vector也可以。
如果修改是任意两点交换可以把vector换成set。
Code:
#include<bits/stdc++.h>
#define maxn 300005
using namespace std;
int n,m,a[maxn];
vector<int>b[maxn];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[a[i]].push_back(i);
int op,l,r,x;
while(m--){
scanf("%d",&op);
if(op==1){
scanf("%d%d%d",&l,&r,&x);
printf("%d\n",upper_bound(b[x].begin(),b[x].end(),r)-lower_bound(b[x].begin(),b[x].end(),l));
}
else{
scanf("%d",&x);
if(a[x]==a[x+1]) continue;
(*lower_bound(b[a[x]].begin(),b[a[x]].end(),x))++;
(*lower_bound(b[a[x+1]].begin(),b[a[x+1]].end(),x+1))--;
swap(a[x],a[x+1]);
}
}
}
T3
题意比较复杂,直接进题面看吧。
兔子越多限制越大,所以字典序最小就肯定是最后一组尽量多。从后往前枚举加兔子,如果矛盾就分组。
K=1直接对权值打标记、枚举矛盾边。
K=2需要判二分图,可以用带权并查集。标记是对权值的,所以需要考虑
2
a
i
=
x
2
2a_i=x^2
2ai=x2的情况,这样的
a
i
a_i
ai不能出现超过两次,如果有两次那么不能再有第三个数和它矛盾。
Code:
#include<bits/stdc++.h>
#define LL long long
#define maxn 135005
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
template<class T>inline void read(T &a){
char c;while(!isdigit(c=getc()));
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
template<class T>inline void write(T x){
if(x>=10) write(x/10);
putchar(x%10+48);
}
int n,K,a[maxn],ans[maxn],f[maxn],m,vis[maxn];
bool pan[maxn],d[maxn];
int find(int x){
if(x!=f[x]){
int fa=f[x]; f[x]=find(f[x]);
d[x]^=d[fa];
}
return f[x];
}
int main()
{
read(n),read(K);
for(int i=1;i<=n;i++) read(a[i]);
const int Mx = *max_element(a+1,a+1+n);
for(int i=2;i*i<=2*Mx;i+=2) pan[i*i/2]=1;
ans[0]=n;
if(K==1){
for(int i=n;i>=1;i--){
bool flg=0;
for(int j=int(sqrt(a[i])+1);j*j<=Mx+a[i];j++) if(vis[j*j-a[i]]) {flg=1;break;}
if(flg==1) {for(int j=i+1;j<=ans[m];j++) vis[a[j]]=0;ans[++m]=i;}
vis[a[i]]=1;
}
}
else{
for(int i=1;i<=Mx;i++) f[i]=i,d[i]=0;
for(int i=n,x,y;i>=1;i--){
bool flg=0,same=0,other=0;
for(int j=int(sqrt(a[i])+1),k;j*j<=Mx+a[i];j++) if(vis[k=j*j-a[i]]){
if(vis[k]>1&&pan[k]) {flg=1;break;}
if(k==a[i]) {same=1;continue;}
other=1;
if((x=find(a[i]))!=(y=find(k))) f[x]=y,d[x]=d[a[i]]^d[k]^1;
else if(d[a[i]]==d[k]) {flg=1;break;}
}
if(flg||(same&other)) {for(int j=i+1;j<=ans[m];j++) vis[a[j]]=0,f[a[j]]=a[j],d[a[j]]=0;ans[++m]=i;}
vis[a[i]]++;
}
}
write(m+1),putchar('\n');
for(int i=m;i>=1;i--) write(ans[i]),putchar(i==1?10:32);
}
总结:忘记了两个经典解法,动态开点线段树和带权并查集动态判二分图。第二题直接闷头闷脑就开始分块。。