Noblesse Code - HDU 7311 - Virtual Judge (vjudge.net)
题意:
给定n个数对(a,b),有q个询问(A,B),每次可以对(A,B)操作为(A+B,B)或者(A,A+B),对于每个询问,输出当前的(A,B)能够变换到多少个给定的(a,b)。
思路:
可以发现操作过程是
辗转相减的逆过程
,我们将(a+b,b)记录为左儿子(L),(a,a+b)记录为右儿子(R),(a,b)是父节点,一直往上找最终找到的根节点一定是gcd(a,b),此时每个点可以得到一个字符串作为从根节点到达该点的路径,于是问题转变为有多少个给定的点满足他的根节点为(gcd(a,b),gcd(a,b))且他的路径以(A,B)的路径字符串为前缀。
重点在于排序
字符串,优先以字符串字典序排,然后对于每一个相同的字符查询在当前位置减了几次,即在树的哪一段位置开辟了新节点。
当前询问的字符串为t,二分出最大的小于t的位置为pre,然后给t在末尾加一个大于LR的字符,二分最大的小于新t的位置为l,l-pre就是以t为前缀的字符串个数。
AC代码:
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
const int mod = 998244353;
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define fi first
#define se second
const int inf = 0x3f3f3f3f;
const int N = 3e5 + 50;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
#define SVL pair<string,vector<ll>>
vector<SVL> s[N];//存树,s[i]表示gcd(a,b)为i的树,fi是字符串,se是在fi的方向上走了多少步
map<ll,int>mp;//相当于链式前向星的head
int tot=0;
void get(ll a,ll b,SVL &s){
if(a==b)return;
else if(a>b){
ll k=(a-b-1)/b+1;//防止a=k*b一剪梅
s.fi+='L';s.se.pb(k);
get(a-k*b,b,s);
}
else{
ll k=(b-a-1)/a+1;
s.fi+='R';s.se.pb(k);
get(a,b-a*k,s);
}
}
bool cmp(SVL &a,SVL &b){//按树排序
int i=0,j=0;
ll li=0,lj=0;
int n=a.fi.size(),m=b.fi.size();
string &s=a.fi,&t=b.fi;
vector<ll>&nums=a.se,&numt=b.se;
if(i<n&&j<m){
li=nums[i];lj=numt[j];
}
while(i<n&&j<m){
if(s[i]<t[j])return 1;
else if(s[i]>t[j])return 0;
if(li==lj){
i++;j++;
if(i<n&&j<m){
li=nums[i];lj=numt[j];
}
}else if(li<lj){
lj-=li;i++;
if(i<n)li=nums[i];
}else{
li-=lj;j++;
if(j<m)lj=numt[j];
}
}
if(j==m)return 0;
else return 1;
}
void work() {
int n,q;cin>>n>>q;
mp.clear();
tot=0;
for(int i=1;i<=n;++i){
int a,b;cin>>a>>b;
SVL t;
get(a,b,t);
reverse(t.fi.begin(),t.fi.end());
reverse(t.se.begin(),t.se.end());
ll d = __gcd(a, b);
if (!mp.count(d)) {
mp[d] = ++tot;
s[tot].clear();
}
s[mp[d]].pb(t);
}
for(int i=1;i<=tot;++i){
sort(s[i].begin(),s[i].end(),cmp);
}
while(q--){
int a,b;cin>>a>>b;
SVL t;
get(a,b,t);
reverse(t.fi.begin(),t.fi.end());
reverse(t.se.begin(),t.se.end());
ll d = __gcd(a, b);
int id=mp[d];
int l=0,r=s[id].size();
while(l<r){
int mid=(l+r+1)>>1;
if(cmp(s[id][mid-1],t))l=mid;
else r=mid-1;
}
int pre=l;
l=0,r=s[id].size();t.fi+='Z';t.se.pb(0);
while(l<r){
int mid=(l+r+1)>>1;
if(cmp(s[id][mid-1],t))l=mid;
else r=mid-1;
}
cout<<l-pre<<'\n';
}
}
signed main() {
io;
int t;
cin >> t;
while (t--) {
work();
}
return 0;
}