THUSC2024 简要题解

T1

数位DP,设 \(f_{i,j,0/1}\) 表示考虑到从大到小第 \(i\) 位,当前 \(j\) 二进制中为1的位抵到上界,现在与 \(l\) 大/相等的和, \(g_{i,j,0/1}\) 表示考虑到从大到小第 \(i\) 位,当前 \(j\) 二进制中为1的位抵到上界,现在与 \(l\) 大/相等的方案数,直接每次枚举子集转移即可。

复杂度 \(O(T 3^d log_n)\),题解咋是 \(4^d\) 啊/cf。

T2

考虑判定,容易想到一个DP,\(f_i\) 表示最后一位是颜色 \(i\),前面不限可以满足的最大长度,显然 \(f_{s_i}\)\(\min f_{a-z}+1\)。如果所有的 \(f\) 都大于等于 \(k\) 就合法了。

不难发现每次选的如果不是最小的 \(f\) 就不会变化,所以每次选最小的 \(f\) 即可。

T3

二分答案,维护 \(f_{i,0/1}\) 表示 \(i\) 子树满足条件的情况下往上/下的最小值是多少,之后在父亲处匹配。显然选择的一定排序后向上的是一段前缀,所以直接check并且更新 \(f\) 值就行了。

T4

task 1-3:

按照task5下发文件的形式暴搜

#include<bits/stdc++.h>
using namespace std;
int a[5005],ans=1e9;
int cur[5005];
void dfs(int now,int lim){
//	cout<<now<<" "<<ans<<" "<<lim<<endl;
	if(now>=ans+1){
		return;
	}
	if(a[now-1]==lim){
		for(int i=0;i<now;i++)cur[i]=a[i];
		ans=now-1;
		return;
	}
	if(a[now-1]>lim)return;
	for(int i=now-1;i>=0;i--){
		for(int j=now-1;j>=0;j--){
			a[now]=a[i]+a[j];
			if(a[now]<=a[now-1])continue;
//			cout<<now<<" "<<a[i]<<" "<<a[j]<<" "<<a[now]<<" "<<a[now-1]<<" "<<lim<<" "<<ans<<endl;
			dfs(now+1,lim);
		}
	}
}
void read(int x){
	printf("input(a[%d]);\n",x);
}
void print(int x){
	printf("output(a[%d]);\n",x);
}
void add(int x,int y,int z){
	printf("a[%d]=a[%d]+a[%d];\n",x,y,z);
}
int main(){
	freopen("opt.out","w",stdout);
	a[0]=1;
	read(0);
	print(1);
	for(int i=1;i<=128;i++){
		ans=1e9;
		dfs(1,i);
		read(0);
		for(int j=1;j<=ans;j++){
			bool fl=0;
			for(int k=0;k<j&&!fl;k++){
				for(int l=0;l<j;l++){
					if(cur[j]==cur[k]+cur[l]){
						add(j,k,l);
						fl=1;
						break;
					}
				}
			}
		}
		print(ans);
	}
	return 0;
}

task 4:

容易发现求得相当于 \(a_0\) 乘一个数,经过观察发现那个数是 \(2^{30}\)\(998244353\)

#include<bits/stdc++.h>
using namespace std;
void read(int x){
	printf("input(a[%d]);\n",x);
}
void print(int x){
	printf("output(a[%d]);\n",x);
}
void add(int x,int y,int z){
	printf("a[%d]=a[%d]+a[%d];\n",x,y,z);
}
int main(){
	freopen("opt4.out","w",stdout);
	read(0);
	for(int i=1;i<=30;i++)add(0,0,0);
	print(0);
	return 0;
}

task 5:

区间求和,任何非暴力算法均可通过。

#include<bits/stdc++.h>
using namespace std;
int a[5005],ans=1e9;
int cur[5005];
void read(int x){
	printf("input(a[%d]);\n",x);
}
int tt=0;
void print(int x){
	printf("output(a[%d]);\n",x);
}
void add(int x,int y,int z){
	tt++;
	printf("a[%d]=a[%d]+a[%d];\n",x,y,z);
}
void init(int x,int y){
	printf("a[%d]=a[%d];\n",x,y);
}
int L[5005],R[5005],id[5005];
int sl[25][10005],sr[25][10005],tot=128;
void ztree(int p,int l,int r){
	L[p]=l,R[p]=r;
	id[p]=++tot;
	if(l==r){
		init(id[p],l);
		return;
	}
	int mid=l+r>>1;
	ztree(p*2,l,mid);
	ztree(p*2+1,mid+1,r);
	add(id[p],id[p*2],id[p*2+1]);
}
void solve(int p,int l,int r){
	if(l<=L[p]&&R[p]<=r){
		add(0,0,id[p]);
		return;
	}
	int mid=L[p]+R[p]>>1;
	if(l<=mid)solve(p*2,l,r);
	if(r>mid)solve(p*2+1,l,r);
}
string str;
int main(){
	freopen("opt5.in","r",stdin);
	freopen("opt5.out","w",stdout);
	for(int i=1;i<=128;i++)read(i);
	ztree(1,1,128);
	for(int i=1;i<=138;i++){
		getline(cin,str);
	}
	int cl=1e9,cr=0;
	for(int i=139;i<=6460;i++){
		getline(cin,str);
		if(str[0]=='o'){
//			cout<<cl<<" "<<cr<<" 111\n";
			init(0,1e5);
			solve(1,cl,cr);
			print(0);
			cl=1e9,cr=0;
		}
		else{
			int len=str.length(),h=0;
			for(int i=0;i<len;i++){
				if(str[i]=='[')h=0;
				if('0'<=str[i]&&str[i]<='9'){
					h=h*10+(str[i]-'0');
				}
				if(str[i]==']'){
					if(h==100001||h==100002)break;
					if(h==0||h==100003)continue;
					cl=min(cl,h);
					cr=max(cr,h);
//					cout<<L[h]<<" "<<R[h]<<endl;
				}
			}
		}
	}
	return 0;
}

task 6:

发现下发文件的操作为线段树求和操作。并且长度小询问次数多,所以使用猫树。

#include<bits/stdc++.h>
using namespace std;
int a[5005],ans=1e9;
int cur[5005];
void read(int x){
	printf("input(a[%d]);\n",x);
}
int tt=0;
void print(int x){
	printf("output(a[%d]);\n",x);
}
void add(int x,int y,int z){
	tt++;
	printf("a[%d]=a[%d]+a[%d];\n",x,y,z);
}
void init(int x,int y){
	printf("a[%d]=a[%d];\n",x,y);
}
int L[5005],R[5005];
void ztree(int p,int l,int r){
	L[p]=l,R[p]=r;
	if(l==r){
//		init(p,l);
		return;
	}
	int mid=l+r>>1;
	ztree(p*2,l,mid);
	ztree(p*2+1,mid+1,r);
}
int sl[25][10005],sr[25][10005],tot=1024;
void build(int p,int l,int r,int dep,int ty){
	if(p!=1){
		sl[dep][l]=l,sr[dep][r]=r;
		if(ty){
			for(int i=l+1;i<=r;i++){
				sl[dep][i]=++tot;
				add(sl[dep][i],sl[dep][i-1],i);
			}
		}
		else{
			for(int i=r-1;i>=l;i--){
				sr[dep][i]=++tot;
				add(sr[dep][i],sr[dep][i+1],i);
			}
		}
	}
	if(l==r)return;
	int mid=l+r>>1;
	build(p*2,l,mid,dep+1,0);
	build(p*2+1,mid+1,r,dep+1,1);
}
void solve(int p,int l,int r,int dep){
	if(l==r){
		print(l);
		return;
	}
	int mid=L[p]+R[p]>>1;
	if(mid+1<=l)solve(p*2+1,l,r,dep+1);
	else if(r<=mid)solve(p*2,l,r,dep+1);
	else{
//		cout<<l<<" "<<r<<" "<<L[p]<<" "<<R[p]<<" "<<mid<<endl;
		add(0,sl[dep+1][r],sr[dep+1][l]);
		print(0);
	}
}
string str;
int main(){
	freopen("opt6.in","r",stdin);
	freopen("opt6.out","w",stdout);
	for(int i=1;i<=1024;i++)read(i);
	ztree(1,1,1024);
	build(1,1,1024,1,0);
	for(int i=1;i<=3081;i++){
		getline(cin,str);
	}
	int cl=1e9,cr=0;
	for(int i=3082;i<=396800;i++){
		getline(cin,str);
		if(str[0]=='o'){
//			cout<<cl<<" "<<cr<<" 111\n";
			solve(1,cl,cr,1);
			cl=1e9,cr=0;
		}
		else{
			int len=str.length(),h=0;
			for(int i=0;i<len;i++){
				if(str[i]=='[')h=0;
				if('0'<=str[i]&&str[i]<='9'){
					h=h*10+(str[i]-'0');
				}
				if(str[i]==']'){
					if(h==100001||h==100002)break;
					if(h==0||h==100003)continue;
					cl=min(cl,L[h]);
					cr=max(cr,R[h]);
//					cout<<L[h]<<" "<<R[h]<<endl;
				}
			}
		}
	}
	return 0;
}

task 7:

同样是区间求和,但是要求空间线性,不难想到求逆元,也就是 \(\times 998244352\)

发现直接求快速乘每次要34次还是35次,不足以通过,但是分解为 \(2^{23} \times 7 \times 17\) 就只需要32次了,足以通过。

#include<bits/stdc++.h>
using namespace std;
void read(int x){
	printf("input(a[%d]);\n",x);
}
int tt=0;
void print(int x){
	printf("output(a[%d]);\n",x);
}
void add(int x,int y,int z){
	tt++;
	printf("a[%d]=a[%d]+a[%d];\n",x,y,z);
}
void init(int x,int y){
	printf("a[%d]=a[%d];\n",x,y);
}
int L[100005],R[100005];
void ztree(int p,int l,int r){
	L[p]=l,R[p]=r;
	if(l==r){
		return;
	}
	int mid=l+r>>1;
	ztree(p*2,l,mid);
	ztree(p*2+1,mid+1,r);
}
string str;
void mul(int t){
	int q=0;
	while(t){
		if(t&1){
			if(q)add(4097,0,4097);	
			else init(4097,0),q=1;
		}
		if(t>1)add(0,0,0);
		t>>=1;
	}
}
int main(){
	freopen("opt7.in","r",stdin);
	freopen("opt7.out","w",stdout);
	for(int i=1;i<=4096;i++)read(i);
	ztree(1,1,4096);
	for(int i=2;i<=4096;i++)add(i,i,i-1);
	for(int i=1;i<=12297;i++){
		getline(cin,str);
	}
	int cl=1e9,cr=0,md=998244352,ss=0;
	for(int i=12298;i<=69551;i++){
		getline(cin,str);
		if(str[0]=='o'){
			ss++;
//			cout<<cl<<" "<<cr<<" 111\n";
			if(cl==1)print(cr);
			else{
				init(4097,4098);
				init(0,cl-1);
				mul(7);
				init(0,4097);
				init(4097,4098);
				mul(17);
				init(0,4097);
				init(4097,4098);
				mul(8388608);
				add(4097,cr,4097);
				print(4097);
			}
			cl=1e9,cr=0;
		}
		else{
			int len=str.length(),h=0;
			for(int i=0;i<len;i++){
				if(str[i]=='[')h=0;
				if('0'<=str[i]&&str[i]<='9'){
					h=h*10+(str[i]-'0');
				}
				if(str[i]==']'){
					if(h==100001||h==100002)break;
					if(h==0||h==100003)continue;
					cl=min(cl,L[h]);
					cr=max(cr,R[h]);
//					cout<<L[h]<<" "<<R[h]<<endl;
				}
			}
		}
	}
//	cout<<ss;
	return 0;
}

task 8:

迪利克雷前缀和,套板子。但是需要单独计算 8191 和 8192 的答案

#include<bits/stdc++.h>
using namespace std;
void read(int x){
	printf("input(a[%d]);\n",x);
}
int tt=0;
void print(int x){
	printf("output(a[%d]);\n",x);
}
void add(int x,int y,int z){
	tt++;
	printf("a[%d]=a[%d]+a[%d];\n",x,y,z);
}
void init(int x,int y){
	printf("a[%d]=a[%d];\n",x,y);
}
bool ip[10005];
int main(){
	freopen("opt8.out","w",stdout);
	for(int i=1;i<=8190;i++)read(i);
	for(int i=1;i<=8190;i++){
		if(!ip[i]&&i>1){
			add(i,i,1);
			for(int j=i+i;j<=8190;j+=i)add(j,j,j/i),ip[j]=1;
		}
		print(i);
	}
	read(2);
	add(2,1,2);
	print(2);
	read(1);
	add(1,1,4096);
	print(1);
	return 0;
}

task 9:

子集求和,直接高位前缀和,没什么值得注意的。

#include<bits/stdc++.h>
using namespace std;
void read(int x){
	printf("input(a[%d]);\n",x);
}
int tt=0;
void print(int x){
	printf("output(a[%d]);\n",x);
}
void add(int x,int y,int z){
	tt++;
	printf("a[%d]=a[%d]+a[%d];\n",x,y,z);
}
void init(int x,int y){
	printf("a[%d]=a[%d];\n",x,y);
}
int main(){
	freopen("opt9.out","w",stdout);
	for(int i=0;i<=1023;i++)read(i);
	for(int i=0;i<10;i++){
		for(int s=0;s<1024;s++){
			if((s>>i)&1)add(s,s,s^(1<<i)); 
		}
	}
	for(int i=0;i<=1023;i++)print(i);
	return 0;
}

task 10:

看上去没什么特别的,只能直接优化。

发现输入的量以及输出的量很少,所以有用的操作不多。

先正着扫一遍如果知道答案的操作就用赋值代替。

倒着扫一遍,只求要知道的信息。

然后就过了。

#include<bits/stdc++.h>
using namespace std;
void read(int x){
	printf("input(a[%d]);\n",x);
}
int tt=0;
void print(int x){
	printf("output(a[%d]);\n",x);
}
void add(int x,int y,int z){
	tt++;
	printf("a[%d]=a[%d]+a[%d];\n",x,y,z);
}
void init(int x,int y){
	printf("a[%d]=a[%d];\n",x,y);
}
string str[5005];
int n=4234;
bool tg[5005],out[5005];
int fl[5005];
int main(){
	freopen("opt10.in","r",stdin);
	freopen("opt10.out","w",stdout);
	for(int i=1;i<=n;i++){
		getline(cin,str[i]);
	}
	for(int i=11;i<=74;i++){
		int len=str[i].length();
		int h=0;
		for(int j=0;j<len;j++){
			if('0'<=str[i][j]&&str[i][j]<='9')h=h*10+(str[i][j]-'0');
		}
		read(h);
//		cout<<h<<endl;
		tg[h]=1;
	}
	for(int i=75;i<=4170;i++){
		int a1=0,a2=0,a3=0,h=0;
		int len=str[i].length();
		for(int j=0;j<len;j++){
			if(str[i][j]=='[')h=0;
			if('0'<=str[i][j]&&str[i][j]<='9')h=h*10+(str[i][j]-'0');
			if(str[i][j]==']'){
				if(a1==0)a1=h;
				else if(a2==0)a2=h;
				else a3=h;
			}
		}
		if(tg[a2]==0&&tg[a3]==0){
//			init(a1,0);
			tg[a1]=0;
			fl[i]=0;
		}
		else if(tg[a2]==0){
			tg[a1]=1;
//			init(a1,a3);
			fl[i]=1;
		}
		else if(tg[a3]==0){
			tg[a1]=1;
//			init(a1,a2);
			fl[i]=2;
		}
		else{
//			add(a1,a2,a3);
			tg[a1]=1;
			fl[i]=3;
		}
//		cout<<a1<<' '<<a2<<' '<<a3<<endl;
	}
	for(int i=4171;i<=4234;i++){
		int len=str[i].length();
		int h=0;
		for(int j=0;j<len;j++){
			if('0'<=str[i][j]&&str[i][j]<='9')h=h*10+(str[i][j]-'0');
		}
		out[h]=1;
//		print(h);
	}
	for(int i=4170;i>=75;i--){
		
		int a1=0,a2=0,a3=0,h=0;
		int len=str[i].length();
		for(int j=0;j<len;j++){
			if(str[i][j]=='[')h=0;
			if('0'<=str[i][j]&&str[i][j]<='9')h=h*10+(str[i][j]-'0');
			if(str[i][j]==']'){
				if(a1==0)a1=h;
				else if(a2==0)a2=h;
				else a3=h;
			}
		}
		if(!out[a1]){
			fl[i]=0;
			continue;
		}
		out[a1]=0;
		out[a2]=out[a3]=1;
	}
	for(int i=75;i<=4170;i++){
		int a1=0,a2=0,a3=0,h=0;
		int len=str[i].length();
		for(int j=0;j<len;j++){
			if(str[i][j]=='[')h=0;
			if('0'<=str[i][j]&&str[i][j]<='9')h=h*10+(str[i][j]-'0');
			if(str[i][j]==']'){
				if(a1==0)a1=h;
				else if(a2==0)a2=h;
				else a3=h;
			}
		}
		if(fl[i]==1)init(a1,a3);
		if(fl[i]==2)init(a1,a2);
		if(fl[i]==3)add(a1,a2,a3); 
	}
	for(int i=4171;i<=4234;i++){
		int len=str[i].length();
		int h=0;
		for(int j=0;j<len;j++){
			if('0'<=str[i][j]&&str[i][j]<='9')h=h*10+(str[i][j]-'0');
		}
		print(h);
	}
	return 0;
}
posted @ 2025-02-10 18:44  QZJ123456  阅读(22)  评论(0)    收藏  举报