【做题记录】贪心--提高组
A. Monotone Subsequence
有点 Ad-hoc。
第 \(i\) 次查询,直接询问当前未被删去的所有点。如果回答 \(\ge n+1\),那么直接输出;否则将回答的这些点标一个级别 \(i\)。最后一次询问之后还剩下的标为 \(n+1\)。根据鸽笼原理,如果最后没剩下,那么中间必然有一次回答 \(\ge n+1\)。
可以发现,对于每个级别为 \(i\) 的位置,在其之前必然有一个级别为 \(i-1\) 的位置 \(j\),且 \(p_j\) 是必然比 \(p_i\) 大的。于是我们连边,跑一个 DAG 上 DP 即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=1e4+5;
int T,n,a[maxn],f[maxn],g[maxn];
set<int> st;
vector<int> e[maxn];
queue<int> q;
il void print(int u){
	if(!u){
		printf("! ");
		return ;
	}
	print(g[u]);
	printf("%d ",u);
}
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n);
		st.clear();
		for(int i=1;i<=n*n+1;i++){
			st.insert(i),e[i].clear();
		}
		for(int i=1;i<=n;i++){
			printf("? %d ",(int)st.size());
			for(int j:st){
				printf("%d ",j);
			}
			puts(""),fflush(stdout);
			int c;
			scanf("%d",&c);
			if(c>=n+1){
				printf("! ");
				for(int j=1,x;j<=c;j++){
					scanf("%d",&x);
					if(j<=n+1){
						printf("%d ",x);
					}
				}
				puts(""),fflush(stdout);
				goto togo;
			}
			for(int j=1,x;j<=c;j++){
				scanf("%d",&x);
				a[x]=i,st.erase(x);
			}
		}
		for(int i:st){
			a[i]=n+1;
		}
		for(int i=1;i<=n*n+1;i++){
			if(a[i]==1){
				q.push(i),f[i]=1,g[i]=0;
			}else{
				for(int j=i-1;j;j--){
					if(a[j]==a[i]-1){
						e[j].pb(i);
						break;
					}
				}
			}
		}
		while(q.size()){
			int u=q.front();
			q.pop();
			for(int v:e[u]){
				q.push(v);
				f[v]=f[u]+1,g[v]=u;
			}
		}
		for(int i=1;i<=n*n+1;i++){
			if(f[i]==n+1){
				print(i);
				puts(""),fflush(stdout);
				break;
			}
		}
		togo:;
	}
	return 0;
}
}
int main(){return asbt::main();}
B. Yet Another MEX Problem
重要结论:\(\sum[a_i>\operatorname{mex}(a)]=\max\limits_{x\notin a}\sum[a_i>x]\)。证明显然。
于是我们考虑在移动 \(r\) 时对每个 \(x\) 维护右面那个式子。设最后出现 \(x\) 的位置为 \(p_x\),我们维护的就是 \([p_x+1,r]\) 中大于 \(x\) 的数的个数,这是方便用线段树维护的。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
const int maxn=3e5+5;
int T,n,a[maxn],tr[maxn<<2],tag[maxn<<2];
il void pushup(int id){
	tr[id]=max(tr[lid],tr[rid]);
}
il void pushtag(int id,int v){
	tr[id]+=v,tag[id]+=v;
}
il void pushdown(int id){
	if(tag[id]){
		pushtag(lid,tag[id]);
		pushtag(rid,tag[id]);
		tag[id]=0;
	}
}
il void build(int id,int l,int r){
	tr[id]=tag[id]=0;
	if(l==r){
		return ;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
}
il void upd(int id,int l,int r,int p){
	if(l==r){
		tr[id]=0;
		return ;
	}
	pushdown(id);
	int mid=(l+r)>>1;
	if(p<=mid){
		upd(lid,l,mid,p);
	}else{
		pushtag(lid,1);
		upd(rid,mid+1,r,p);
	}
	pushup(id);
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>T;
	while(T--){
		cin>>n;
		build(1,0,n);
		for(int i=1;i<=n;i++){
			cin>>a[i];
			upd(1,0,n,a[i]);
			cout<<tr[1]<<' ';
		}
		cout<<'\n';
	}
	return 0;
}
}
int main(){return asbt::main();}
C. Make Good
Ad-hoc。
先把 \(n\) 为奇数判掉。然后开始对脑电波寻找性质:
首先,所有的 (( 和 )) 都是可以随意移动的。比如 ((:
- (()\(\to\)- )))\(\to\)- )((
- )((\(\to\)- )))\(\to\)- (()
- (((\(\to\)- ((((没变但等于是动了)
所以我们先用栈把所有的 (( 和 )) 找出来,设有 \(cnt\) 个。剩下的字符串有以下三种可能:
- ()()()...()
- )()()()...()(
- 空串
那么如果 \(cnt\) 为奇数则必然无解,如果为偶数那么可以在这个串左右各加 \(\frac{cnt}{2}\) 个 (( 和 ))。当然如果 \(cnt=0\) 那么第二种情况就不成立。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=2e5+5;
int T,n,top,cnt;
char stk[maxn];
string s;
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>T;
	while(T--){
		cin>>n>>s;
		if(n&1){
			cout<<-1<<'\n';
			continue;
		}
		cnt=top=0;
		for(int i=0;i<n;i++){
			if(!top){
				stk[++top]=s[i];
			}else if(stk[top]==s[i]){
				cnt++,top--;
			}else{
				stk[++top]=s[i];
			}
		}
		if(cnt&1){
			cout<<-1<<'\n';
		}else if(!cnt&&stk[1]==')'){
			cout<<-1<<'\n';
		}else{
			for(int i=1;i<=cnt>>1;i++){
				cout<<"((";
			}
			for(int i=1;i<=top;i++){
				cout<<stk[i];
			}
			for(int i=1;i<=cnt>>1;i++){
				cout<<"))";
			}
			cout<<'\n';
		}
	}
	return 0;
}
}
int main(){return asbt::main();}
D. Long Journey
设 \(len=\operatorname{lcm}(a)\)。显然 \(len\le2520\)。称走 \(len\) 格为走了一个周期。
考虑整个过程,就是走 \(\lfloor\frac{m}{len}\rfloor\) 个周期,然后再走 \(m\bmod len\) 步。考虑压缩前面那些周期的过程。枚举进入这个周期前的时刻模 \(n\) 的值 \(i\),我们希望求出此状态下走一个周期的用时。模拟一遍这个周期的过程即可。
模拟的过程是一个贪心:如果下一格在下一秒是陷阱,那就等一秒,否则就走过去。如果在一格等待了 \(n\) 秒还是没走,那么无解。贪心的正确性在于不会出现两个相邻的格子同时是陷阱的情况。在每个格子都最多等待 \(n\) 次,所以单次模拟的时间复杂度是 \(O(n\times len)\) 的。这个预处理的总时间是 \(O(n^2\times len)\)。
于是我们就可以快速计算了。走周期这个过程显然可合并,于是我们对走周期这个过程做快速幂。合并两个过程是 \(O(n)\) 的,这一部分时间复杂度 \(O(n\log\lfloor\frac{m}{len}\rfloor)\)。然后再类似上面的方式模拟最后一小段即可。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define gcd __gcd
using namespace std;
namespace asbt{
const int inf=1e18;
int T,n,m,a[12],b[12];
struct juz{
	int a[12];
	il int&operator[](int x){
		return a[x];
	}
	il juz operator*(juz b)const{
		juz c;
		for(int i=0;i<n;i++){
			c[i]=min(a[i]+b[(i+a[i])%n],inf);
		}
		return c;
	}
}bas;
il int calc(int t,int len){
	int p=0,res=0;
	while(p<len){
		p++;
		int cnt=0;
		t=t%n+1,res++;
		while(p%a[t]==b[t]){
			if(cnt==n){
				return inf;
			}
			t=t%n+1,cnt++,res++;
		}
	}
	return res;
}
il juz qpow(juz x,int y){
	juz res;
	for(int i=0;i<n;i++){
		res[i]=0;
	}
	while(y){
		if(y&1){
			res=res*x;
		}
		x=x*x,y>>=1;
	}
	return res;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>T;
	while(T--){
		cin>>n>>m;
		int len=1;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			len=len/gcd(len,a[i])*a[i];
		}
		for(int i=1;i<=n;i++){
			cin>>b[i];
		}
		if(m<len){
			int ans=calc(0,m);
			cout<<(ans>=inf?-1:ans)<<'\n';
			continue;
		}
		for(int i=0;i<n;i++){
			bas[i]=calc(i,len);
//			cout<<bas[i]<<' ';
		}
//		cout<<'\n';
		int ans=qpow(bas,m/len)[0];
		ans=ans+calc(ans%n,m%len);
		cout<<(ans>=inf?-1:ans)<<'\n';
	}
	return 0;
}
}
signed main(){return asbt::main();}
E. I Yearned For The Mines
考虑如果是一条链,我们从一头查到另一头就行了。否则就必须短边。也就是说我们要在 \(\lfloor\frac{n}{4}\rfloor\) 次内将树断成若干条链。这是必然可行的,因为任意三个点就是链,所以最坏情况每四个点就要断一个点。链分为一直往上和先上后下(跨过 lca)两种,于是我们从下往上贪心即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=2e5+5;
int T,n,fa[maxn],son[maxn],tot;
bool f[maxn],vis[maxn];
vector<int> e[maxn];
struct{
	int d1,d2,dd;
}a[maxn];
il void dfs(int u,int fth){
	fa[u]=fth;
	int cnt=0;
	for(int v:e[u]){
		if(v==fth){
			continue;
		}
		dfs(v,u);
		if(!f[v]){
			cnt++;
		}
	}
	if(f[u]){
		for(int v:e[u]){
			if(v!=fth&&!vis[v]&&!f[v]){
				a[++tot]={son[v],son[v],v};
			}
		}
		return ;
	}
	if(cnt==0){
		son[u]=u;
		return ;
	}else if(cnt==1){
		for(int v:e[u]){
			if(v!=fth&&!f[v]){
				son[u]=son[v];
				return ;
			}
		}
	}else if(cnt==2){
		if(fth&&!f[fth]){
			f[fth]=1;
		}
		a[++tot].dd=u,vis[u]=1;
		for(int v:e[u]){
			if(v==fth||f[v]){
				continue;
			}
			if(a[tot].d1){
				a[tot].d2=son[v];
			}else{
				a[tot].d1=son[v];
			}
		}
	}else{
		f[u]=1;
		for(int v:e[u]){
			if(v!=fth&&!f[v]){
				a[++tot]={son[v],son[v],v};
			}
		}
	}
//	if(tot>n){
//		exit(114514);
//	}
}
il void print(int u,int v){
//	if(v<1||v>n){
//		exit(v?v:998244353);
//	}
//	if(u<1||u>n){
//		exit(514);
//	}
	if(u==v){
		return ;
	}
	print(fa[u],v);
	cout<<1<<' '<<u<<'\n';
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=1,u,v;i<n;i++){
			cin>>u>>v;
			e[u].pb(v),e[v].pb(u);
		}
		dfs(1,0);
		if(!vis[1]&&!f[1]){
			a[++tot]={son[1],son[1],1};
		}
		int cnt=0;
		for(int i=1;i<=n;i++){
			if(f[i]){
				cnt++;
			}
		}
		cout<<n+cnt<<'\n';
		for(int i=1;i<=n;i++){
			if(f[i]){
				cout<<1<<' '<<i<<'\n'<<2<<' '<<i<<'\n';
			}
		}
		for(int i=1;i<=tot;i++){
			if(a[i].d1==a[i].d2){
				int u=a[i].d1;
				cout<<1<<' '<<u<<'\n';
				if(u==a[i].dd){
					continue;
				}
				do{
					u=fa[u];
					cout<<1<<' '<<u<<'\n';
				}while(u!=a[i].dd);
			}else{
				int u=a[i].d1;
				cout<<1<<' '<<u<<'\n';
				do{
					u=fa[u];
					cout<<1<<' '<<u<<'\n';
				}while(u!=a[i].dd);
				print(a[i].d2,a[i].dd);
			}
		}
		for(int i=1;i<=n;i++){
			fa[i]=son[i]=f[i]=vis[i]=0;
			e[i].clear();
		}
		for(int i=1;i<=tot;i++){
			a[i]={0,0,0};
		}
		tot=0;
	}
	return 0;
}
}
int main(){return asbt::main();}
//
F. Traffic Lights
猜结论题,证明 AAAAAd-hoooooc。
结论:最短的总时间不会超过 \(3n\)。
Proof:对于一条路径,总的时间不会超过 \(\sum deg\),因为最多在一个点等一圈后走出去。设 \(\sum deg=d\)。考虑从 \(1\) 到 \(n\) 的最短路,首先在路径上的不相邻的点之间不会有边;其次,对于不在路径上的点,因为无重边,所以不可能链接路径上的 \(4\) 个及以上的点。那么设路径上的点数为 \(k\),则 \(d=3(n-k)+2k=3n-k<3n\)。
于是可以 DP,设 \(f_{i,j}\) 表示 \(i\) 时刻在 \(j\) 的最小等待次数即可。需要滚动数组。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=5e3+5,inf=1e9;
int T,n,m,f[2][maxn],d[maxn];
vector<int> e[maxn];
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>T;
	while(T--){
		cin>>n>>m;
		for(int i=1,u,v;i<=m;i++){
			cin>>u>>v;
			e[u].pb(v),e[v].pb(u);
			d[u]++,d[v]++;
		}
		for(int i=1;i<=n;i++){
			f[0][i]=inf;
		}
		f[0][1]=0;
		for(int i=0,p=0,q=1;i<n*3;i++,p^=1,q^=1){
			for(int i=1;i<=n;i++){
				f[q][i]=inf;
			}
			for(int j=1;j<=n;j++){
				f[q][j]=min(f[q][j],f[p][j]+1);
				int k=e[j][i%d[j]];
				f[q][k]=min(f[q][k],f[p][j]);
			}
			if(f[q][n]<inf){
				cout<<i+1<<' '<<f[q][n]<<'\n';
				break;
			}
		}
		for(int i=1;i<=n;i++){
			d[i]=0,e[i].clear();
		}
	}
	return 0;
}
}
int main(){return asbt::main();}
G. Puzzle
对于一个确定的矩形,设长、宽为 \(x,y\),在其变为一个 L 形图案的过程中它的周长是不变的。而整个过程中的面积范围是确定的,即为 \(x+y-1\) 到 \(x\times y\)。
于是考虑枚举所有可能的周长 \(n\)。对于确定的周长 \(n\),L 形图案的面积为固定的 \(\frac{n}{2}-1\)。我们希望能覆盖尽可能大的面积范围,于是取 \(x=\lfloor\frac{n}{4}\rfloor,y=\lceil\frac{n}{4}\rceil\),考察此时的面积范围是否覆盖了我们需要的面积即可。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define gcd __gcd
using namespace std;
namespace asbt{
int T,n,m; 
il bool solve(int n,int m){
	if(n&1){
		return 0;
	}
	int x=n/4,y=n/2-x;
	if(m<x+y-1||m>x*y){
		return 0;
	}
	cout<<m<<'\n';
	for(int i=0;i<x;i++){
		cout<<0<<' '<<i<<'\n';
	}
	for(int i=1;i<y;i++){
		cout<<i<<' '<<0<<'\n';
	}
	if(y==1){
		return 1;
	}
	m-=x+y-1;
	for(int i=1;i<=m/(y-1);i++){
		for(int j=1;j<y;j++){
			cout<<j<<' '<<i<<'\n';
		}
	}
	for(int i=1;i<=m%(y-1);i++){
		cout<<i<<' '<<m/(y-1)+1<<'\n';
	}
	return 1;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>T;
	while(T--){
		cin>>n>>m;
		int gg=gcd(n,m);
		n/=gg,m/=gg;
		for(int i=1;m*i<=5e4;i++){
			if(solve(n*i,m*i)){
				goto togo;
			}
		}
		cout<<-1<<'\n';
		togo:;
	}
	return 0;
}
}
signed main(){return asbt::main();}
H. Cycling (Easy Version)
如果 \(p\) 是 \(1\sim p\) 中最小的且第一次出现,那么我们可以一路带着 \(p\) 超,显然一定是最优的。于是可以 DP:设 \(dp_i\) 表示到 \(i\) 后面的最小花费。记 \(p\) 是 \(i\) 后面第一个最小值的位置,于是有转移:
答案即为 \(dp_0\)。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=5e3+5,inf=1e18;
int T,n,a[maxn],dp[maxn];
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i];
		}
		dp[n]=0;
		for(int i=n-1;~i;i--){
			int p=i+1;
			for(int j=i+1;j<=n;j++){
				if(a[j]<a[p]){
					p=j;
				}
			}
			dp[i]=inf;
			for(int j=p;j<=n;j++){
				dp[i]=min(dp[i],dp[j]+2*(j-p)+(p-i-1)+a[p]*(j-i));
			}
		}
		cout<<dp[0]<<'\n';
	}
	return 0;
}
}
signed main(){return asbt::main();}
I. 23 Kingdom
最优的 \(b\) 序列一定是前面一些位置填了 \(1\sim k\),后面一些位置填了 \(1\sim k\),中间乱填的一个状态。于是我们只要从前面和后面分别贪心地选择一些位置就好了。不妨只考虑前面那些位置,设它们组成的集合为 \(S\),其合法的充要条件为 \(\forall i\in[1,n],\sum_{x\in S}[a_x\le i]\le i\),因为若不满足则根据鸽巢原理必然有相同的 \(b_x\),若满足则显然可以构造。于是用线段树维护 \(i-\sum_{x\in S}[a_x\le i]\) 的最小值即可。
Code
#include<bits/stdc++.h>
#define int long long
#define il inline
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
const int maxn=2e5+5;
int T,n,a[maxn],tr[maxn<<2],tag[maxn<<2];
int b[maxn],c[maxn];
il void pushup(int id){
	tr[id]=min(tr[lid],tr[rid]);
}
il void pushtag(int id,int v){
	tr[id]+=v,tag[id]+=v;
}
il void pushdown(int id){
	if(tag[id]){
		pushtag(lid,tag[id]);
		pushtag(rid,tag[id]);
		tag[id]=0;
	}
}
il void build(int id,int l,int r){
	tag[id]=0;
	if(l==r){
		tr[id]=l;
		return ;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
	pushup(id);
}
il void add(int id,int L,int R,int l,int r,int v){
	if(L>=l&&R<=r){
		pushtag(id,v);
		return ;
	}
	pushdown(id);
	int mid=(L+R)>>1;
	if(l<=mid){
		add(lid,L,mid,l,r,v);
	}
	if(r>mid){
		add(rid,mid+1,R,l,r,v);
	}
	pushup(id);
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>T;
	while(T--){
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i];
		}
		build(1,1,n);
		int cb=0;
		for(int i=n;i;i--){
			add(1,1,n,a[i],n,-1);
			if(tr[1]>=0){
				b[++cb]=i;
			}else{
				add(1,1,n,a[i],n,1);
			}
		}
		build(1,1,n);
		int cc=0;
		for(int i=1;i<=n;i++){
			add(1,1,n,a[i],n,-1);
			if(tr[1]>=0){
				c[++cc]=i;
			}else{
				add(1,1,n,a[i],n,1);
			}
		}
		int ans=0;
		for(int i=1;i<=min(cb,cc)&&c[i]<b[i];i++){
			ans+=b[i]-c[i];
		}
		cout<<ans<<'\n';
	}
	return 0;
}
}
signed main(){return asbt::main();}
J. Wonderful Teddy Bears
所有操作的过程就是将逆序对数减少到 \(0\) 的过程。考虑能使逆序对减少最多的操作,即为 \(\texttt{PBB}\to\texttt{BBP}\) 或 \(\texttt{PBB}\to\texttt{BBP}\),能使逆序对减少两个。但它的操作次数是不好算的,考虑尽可能地进行了这个操作之后序列的形态,即为 \(\texttt{BBBBBB}\dots\texttt{BPBPBP}\dots\texttt{BPBP}\dots\texttt{PPPPP}\),于是我们还需要进行一部分 \(\texttt{BPB}\to\texttt{BBP}\)、\(\texttt{PBP}\to\texttt{BPP}\) 的操作,会使逆序对减一。考虑这类操作的数量,发现 \(\texttt{PBB}\to\texttt{BBP}\) 操作不会改变偶数位上 \(\texttt{B}\) 的数量,而 \(\texttt{BPB}\to\texttt{BBP}\) 会改变。设总共有 \(a\) 个 \(\texttt{B}\),偶数位上有 \(b\) 个 \(\texttt{B}\),于是第二类操作就一共有 \(|\lfloor\frac{a}{2}\rfloor-b|\) 次,于是第一类操作就有 \(\frac{tot-|\lfloor\frac{a}{2}\rfloor-b|}{2}\) 次,其中 \(tot\) 是逆序对的数量。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
int T,n;
string s; 
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>T;
	while(T--){
		cin>>n>>s;
		ll cnt=0,tot=0,ctt=0,cev=0;
		for(int i=0;i<n;i++){
			if(s[i]=='B'){
				tot+=cnt,ctt++;
				if(i&1){
					cev++;
				}
			}else{
				cnt++;
			}
		}
		cout<<(tot-abs(ctt/2-cev))/2+abs(ctt/2-cev)<<'\n';
	}
	return 0;
}
}
int main(){return asbt::main();}
K. Zebra-like Numbers
首先考虑如何求一个数的斑马值。考虑贪心,即不停地选择最大的斑马数减去。考虑正确性。
假设 \(x\) 是最小的不能使用上面那个贪心求出斑马值的数,记小于它的最大的斑马数为 \(z_i\),故 \(x\) 的分解中必然没有 \(z_i\) 并且有 \(z_{i-1}\)。由于 \(z_i=4z_{i-1}+1\),所以 \(x\) 中含有 \(4\) 个 \(z_{i-1}\),和至少一个其它斑马数。如果第 \(5\) 个数是 \(1\),那么我们就能凑出一个 \(z_i\),否则就能凑出一个 \(z_i\) 和另外四个更小的数,这样得到的斑马值是不劣的。故 \(x\) 不存在。
那么我们考虑用一种奇怪的进制表示 \(x=\sum t_iz_i\),于是有 \(t_i\le4\),并且如果 \(t_i=4\) 那么 \(\forall j<i,t_j=0\)。数位 DP 即可。
Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
int T,dig[39];
ll l,r,k,bas[39],f[39][97][2][2];
il ll dfs(int pos,int sum,bool limit,bool four){
//	cout<<pos<<' '<<sum<<' '<<limit<<' '<<four<<'\n';
	if(sum>k){
		return 0;
	}
	if(!pos){
		return sum==k;
	}
	if(~f[pos][sum][limit][four]){
		return f[pos][sum][limit][four];
	}
	ll &res=f[pos][sum][limit][four];
	if(four){
		return res=dfs(pos-1,sum,limit,four);
	}
	res=0;
//	cout<<(limit?dig[pos]:4)<<'\n';
	for(int i=0;i<=(limit?dig[pos]:4);i++){
		res+=dfs(pos-1,sum+i,limit&&i==dig[pos],i==4);
	}
	return res;
}
il ll solve(ll x){
//	cout<<x<<":\n";
	memset(f,-1,sizeof(f));
	for(int i=30;i;i--){
		dig[i]=x/bas[i];
		x%=bas[i];
	}
//	for(int i=1;i<=30;i++){
//		cout<<dig[i]<<' ';
//	}
//	cout<<'\n';
	return dfs(30,0,1,0);
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	bas[1]=1;
	for(int i=2;i<=30;i++){
		bas[i]=bas[i-1]<<2|1;
	}
	cin>>T;
//	ofstream cout("my.out");
	while(T--){
		cin>>l>>r>>k;
		solve(r),solve(l-1);
		cout<<solve(r)-solve(l-1)<<'\n';
	}
	return 0;
}
}
int main(){return asbt::main();}

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号