1[CSP-J 2024] 小木棍

错因:bfs暴力本身就有问题,时间和空间肯定过不了,不过思路是正确的,题目特性也暗示了7的倍数

正确做法:

#include<bits/stdc++.h>

using namespace std;
//0 1 2 3 4 5 6 7 8 9
//6 2 5 5 4 5 6 3 7 6
int main(){
	int t;
	cin>>t;
	//思路: 
	//可以发现8用的棍数最多,所以8越多,位数越少,值越少
	//所以尽量多凑8出来 
	while(t--){
		int n;
		cin>>n;
		if(n==1)cout<<-1<<'\n';
		//可以手动推出n为1-9时的最小值 
		else if(n==2)cout<<1<<'\n';
		else if(n==3)cout<<7<<'\n';
		else if(n==4)cout<<4<<'\n';
		else if(n==5)cout<<2<<'\n';
		else if(n==6)cout<<6<<'\n';
		else if(n==7)cout<<8<<'\n';
		//10要特别判断,因为最小值并不是78 
		else if(n==10)cout<<22<<'\n';
		//后续通过计算%7(8的用棍数为7)后的余数,计算用棍数为余数的最小值
		//再加上剩下整除7的次数的8 
		else if(n%7==0){
			for(int i=1;i<=n/7;i++){
				cout<<8;
			}
			cout<<'\n';
		}else if(n%7==1){
			cout<<10;
			for(int i=1;i<=(n-8)/7;i++){
				cout<<8; 
			}
			cout<<'\n';
		}else if(n%7==2){
			cout<<1;
			for(int i=1;i<=(n-2)/7;i++){
				cout<<8;
			}
			cout<<'\n';
		}else if(n%7==3){
			cout<<200;
			for(int i=1;i<=(n-17)/7;i++){
				cout<<8;
			}
			cout<<'\n';
		}else if(n%7==4){
			cout<<20;
			for(int i=1;i<=(n-11)/7;i++){
				cout<<8;
			}
			cout<<'\n';
		}else if(n%7==5){
			cout<<2;
			for(int i=1;i<=(n-5)/7;i++){
				cout<<8;
			}
			cout<<'\n';
		}else if(n%7==6){
			cout<<6;
			for(int i=1;i<=(n-6)/7;i++){
				cout<<8;
			}
			cout<<'\n';
		} 
	}
	return 0;
} 

2[CSP-J 2022] 解密

错因:跟正解相差极大,又是暴力失败的一次,没有找到方程定值(完全平方公式)

正确做法1:

  #include<bits/stdc++.h>

using namespace std;

int main(){
	//freopen("decode.in","r",stdin);
	//freopen("decode.out","w",stdout);
	int t;
	cin>>t;
	while(t--){
		long long n,d,e;
		cin>>n>>d>>e;
		// 根据题面可得 e*d=(p-1)*(q-1)+1=e*d=pq-p-q+2 
		// 带入n=pq; 
		//化简得:p+q=n-e*d+2 
		//完全平方公式:完全平方和-完全平方差
		//解得 p-q=sqrt(pow(n-ed+2,2)-4n)
		long sum=n-e*d+2;//p+q
		long long p=(sqrt(sum*sum-4*n)+sum)/2;
		long long q=sum-p;
		//前面的表达式中间运算未必准确的整数,所以注意校验
		if(p*q==n)cout<<min(p,q)<<" "<<max(p,q)<<'\n'; 
		else cout<<"NO"<<'\n';
	}
	return 0;
}

正确解法2:

#include<bits/stdc++.h>

using namespace std;

int main(){
	int k;
	cin>>k;
	while(k--){
		long long n,d,e;
		cin>>n>>d>>e;
		//p+q=n-d*e+2//化简所得
		long long sumpq=n-d*e+2; 
    //l,r不为0且p>=q所以sumpq/2>=2q为寻找q值
		long long l=1,r=(n-d*e+2)/2;
		while(l<r){
			int mid=(l+r)/2;
			if(mid*(sumpq-mid)>=n){
				r=mid;
			}else{
				l=mid+1;
			}
		}
		if(l*(sumpq-l)==n)cout<<l<<" "<<sumpq-l<<'\n';
		else cout<<"NO"<<'\n';
	} 
	return 0;
} 

3[CSP-J 2022] 上升点列

错因:。。。(还没做,所以没出错)

正确做法:

#include<bits/stdc++.h>

using namespace std;
int n,k;
struct node{
	int x,y;
	bool operator <(const node &o)const{
		if(x!=o.x){
			return x<o.x;
		}
		return y<o.y;
	} 
};
node a[100010];
int f[510][110];
//dp[i][j]表示枚举到i时,还可以选j次自由点机会时的最大长度 
int main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>a[i].x>>a[i].y;
	}
	sort(a+1,a+1+n);//从小到大枚举边界 
	for(int i=1;i<=n;i++){
		for(int j=0;j<=k;j++){
			f[i][k]=1;
			for(int s=1;s<i;s++){//枚举前面的点 
				//题目要求横坐标、纵坐标值均单调不减,不符合要求要跳过 
				if(a[i].x<a[s].x||a[i].y<a[s].y)continue;
				int disx=abs(a[i].x-a[s].x);
				int disy=abs(a[i].y-a[s].y);
				//两点之间的距离使用曼哈顿距离计算
				//但需要减掉两边的交点 
				int zdis=disx+disy-1;
				//需要判断当前联通需要的点数是否<k 
				if(j+zdis>k)continue;
				//f[s][j+zdis]+zdis+1表示第1->i-1;
				//第i个结点以内增加zdis个自由点增加距离推到过来 
				f[i][j]=max(f[i][j],f[s][j+zdis]+zdis+1); 
					
			} 
		}
	}
	int maxs=0;
	for(int i=1;i<=n;i++){
		for(int j=0;j<=k;j++){
			maxs=max(maxs,f[i][j]+j);
		}
	}
	cout<<maxs;
	return 0;
} 

4[CSP-J 2023] 旅游巴士

正确做法:

  #include<bits/stdc++.h>

using namespace std;
struct node{
	int v,w;
};
struct nobe{
	int x,cnt;
	bool operator < (const nobe& o)const{
		return cnt>o.cnt;
	}
};
vector<node> c[2000010];
int n,m,k;
bool vis[11000][110];
int ans=0;
int bfs(int u){
	priority_queue<nobe> q;
	//优先队列可以按照时间排序
	q.push({u,0});
	while(!q.empty()){
		nobe u=q.top();
		q.pop();
		//题目要求离开时刻为k的倍数,因此要特判 
		if(u.x==n&&u.cnt%k==0){
			return u.cnt;
		}
		//按时间先访问的元素先vis标记,不然有些先访问到的结点会被后面的vis排除 
		if(vis[u.x][u.cnt%k])continue;
		vis[u.x][u.cnt%k]=1;
		for(node s:c[u.x]){
			int v=s.v; 
			//走到当前点的时刻
			int nowcnt=u.cnt+1; 
			//如果这个结点的开放时刻>=走过来的通行时间,则需要增加等待时刻变为k的倍数的时间 
			if(s.w>=u.cnt+1){ 
				//计算等待时间
				int wait=((s.w-nowcnt)/k+1)*k; 
				nowcnt+=wait; 
			}
			//题目说明了对于每条道路,通行时间为1单位 
			q.push({v,nowcnt}); 			
		}
	}
	return -1;
}
int main(){
	//freopen("bus.in","r",stdin);
	//freopen("bus.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie();
	cout.tie();
	cin>>n>>m>>k;
	for(int i=1;i<=m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		c[u].push_back({v,w});
	}
	cout<<bfs(1);
	return 0;
}

5[CSP-S2020] 动物园

思路:开一个check变量记录出现1的位数再去除需要新饲料的位数,除去一定不能为1位,剩下k个位可能为 1,那总动物数就能达到2的k次方,减去已经有的n个动物,所以答案是 pow(2,k)−n。

正确做法:

  #include<bits/stdc++.h>

using namespace std;

typedef unsigned long long LL;
LL hav=0;
LL a[1000010];
int main(){
	//freopen("zoo.in","r",stdin);
	//freopen("zoo.ans","w",stdout);
	//注意要特判 k=64,n=0的情况,读入量较大,需要写快读
	ios::sync_with_stdio(0);
	cin.tie();
	cout.tie(); 
	int n,m,c,k;
	cin>>n>>m>>c>>k;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		hav|=a[i];
	}
	LL g=0;//记录需要新饲料的动物数量 
	while(m--){
		int p,q;
		cin>>p>>q;
		//判断 p点是否被覆盖过 
		if((hav>>p&1)==0){
			g|=1ULL<<p;
		}
	}
	LL ans=1;
	//总共有2的k次方只动物可以选 
	//除去一定不能为1的位,剩下t个位可能为1 
	for(int i=0;i<k;i++){
		if((g>>i&1)==0)ans<<=1;
	} 
	ans-=n;
	//如果n==0 ,t==64,long long 会溢出 
	if(ans==0&&n==0){
		cout<<"18446744073709551616";
		return 0;
	}
	cout<<ans; 
	return 0;
} 

6[CSP-J 2023] 一元二次方程

注意事项:检查每一个输出格式(肥肠恶心),p1和p2输出时千万万万万万要小心!!!!!!

思路:按题意模拟即可(太不容易了),具体步骤看题解,有个约分的方法很好用,具体看题解(标记为$$$)

#include <bits/stdc++.h>
using namespace std;
long long T, M;

long long gcdx(long long a, long long b) {
	if (b == 0)
		return a;
	else
		return gcdx(b, a % b);
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie();
	cout.tie();
	cin >> T >> M;
	while (T--) {
		long long a, b, c;
		cin >> a >> b >> c;
		long long sumabc = b * b - 4 * a * c;
		if (sumabc < 0) { //无解
			cout << "NO" << '\n';
		} else if (sumabc == 0) {
			//只有一个解,为(-b)/(2*a)
			long long p = -b, q = 2 * a;
			long long pq = gcdx(abs(p), abs(q)); //化简
			p /= pq, q /= pq;
      //$$$可以通过分母和分子同时除以他们的最大公因数化简
			//由有理数的定义,存在唯一的两个整数p和q,满足q>0
			if (q < 0) {
				q = -q, p = -q;
			}
			//若q=1,则输出{p},否则输出{p}/{q},其中{n}代表整数n的值;
			if (q == 1) {
				cout << p << '\n';
			} else {
				cout << p << "/" << q << '\n';
			}
		} else {
			//否则b*b-4*a*c>0,此时方程有两解
			//记其中较大者为x,若x为有理数,则按有理数的格式输x
			long long p = -b, q = 2 * a;
			long long sqsum = (long long)sqrt(sumabc);
			//若 x 为有理数,则按有理数的格式输出 x
			if (sqsum * sqsum == sumabc) {
				if (q > 0)
					p += sqsum;
				else
					p -= sqsum;
				long long pq = gcdx(abs(p), abs(q));
				p /= pq, q /= pq;
				if (q < 0) {
					q = -q, p = -p;
				}
				if (q == 1) {
					cout << p << '\n';
				} else {
					cout << p << "/" << q << '\n';
				}
			} else {
				//否则根据上文公式,x可以被唯一表示为x=q1+q2+q2*sqrt(r)的形式
				long long pq = gcdx(abs(p), abs(q));
				p /= pq, q /= pq;
				if (q < 0) {
					q = -q, p = -p;
				}
				if (p != 0) {
					//若q1!=0,则按有理数的格式输出q1,并再输出一个加号+
					if (q == 1) {
						cout << p << '+' ;
					} else {
						cout << p << '/' << q << '+' ;
					}
				}
				q = abs(2 * a);
				p = 1;
				long long t = 0; //t代表题面中的r
				//r 为正整数且 r>1,且不存在正整数d>1使d2|r(即r不应是d*d的倍数)
				for (int r = sqsum; r >= 1; r--) {
					if (sumabc % (r * r) == 0) {
						p *= r;
						t = sumabc / (r * r);
						break;
					}
				}
				pq = gcdx(abs(p), abs(q));
				q /= pq, p /= pq;
				if (q < 0) {
					q = -q, p = -p;
				}
				//若q2=1,则输出 sqrt({r})
				//否则若q2为整数,则输出 {q2}*sqrt({r})
				//否则若 q3=1/q2 为整数,则输出 sqrt({r})/{q3}
				//否则可以证明存在唯一整数c,d满足 c,d>1,gcd(c,d)=1且q2=c/d,此时输出{c}*sqrt({r})/{d}
				if (p == q) {
					cout << sqrt(t) << '\n';
				} else if (q == 1) {
					cout << p << "*" << "sqrt(" << t << ")" << '\n';
				} else if (p == 1) {
					cout << "sqrt(" << t << ")" << "/" << q << '\n';
				} else {
					cout << p << "*sqrt(" << t << ")/" << q << '\n';
				}
			}
		}
	}
	return 0;
}

7[CSP-J 2024] 接龙

思路 :

思路直达

#include <bits/stdc++.h>
using namespace std;
vector<int> num[2000010];
//num[i]表示第i个人的整数序列词库
long long dp[105][200005];

//dp[i][j]表示第i轮,最后一个元素恰好为j是否成立
int main() {
	ios::sync_with_stdio(0);
	cin.tie();
	cout.tie();
	int T;
	cin >> T;
	while (T--) {
		int n, k, q;
		cin >> n >> k >> q;
		memset(dp, -1, sizeof(dp));
		for (int i = 1; i <= n; i++) {
			int l;
			cin >> l;
			num[i].clear();
			//清除上一组的数据
			for (int j = 1; j <= l; j++) {
				int s;
				cin >> s;
				num[i].push_back(s);
			}
		}
		dp[0][1] = 0;
		//初始化第0轮结尾元素为1的方案数为0
		for (int t = 1; t <= 100; t++) { //最多只有100轮
			for (int i = 1; i <= n; i++) {
				int len = 0;
				//冷却计数器(判断第i个人在本轮是否可以接龙)
				for (auto x : num[i]) {
					len = max(len - 1, 0);
					//冷却缩减
					if (len > 0) { //第i个人不能接龙时
						if (dp[t][x] == -1) {
							//如果没有人标记结尾元素为i,就标记
							dp[t][x] = i;
						} else if (dp[t][x] && dp[t][x] != i) {
							//如果有其他人标记,设为冲突状态0
							dp[t][x] = 0;
						}
					}
					// 如果前一回合该数字被其他人标记过
					if (dp[t - 1][x] != -1 && dp[t - 1][x] != i) {
						//重置冷却
						len = k;
					}
				}
			}
		}
		while (q--) {
			int r, c;
			cin >> r >> c;
			//通过查询dp[r][c]来判断是否成立
			if (dp[r][c] != -1) {
				cout << 1 << '\n';
			} else {
				cout << 0 << '\n';
			}
		}
	}
	return 0;
}

8[CSP-J 2022] 逻辑表达式

#include <bits/stdc++.h>
using namespace std;
const int N =1e6 + 5;
char c[N];
int l1[N], l2[N], c1[N], c2[N];
//l1[i] 代表目前最后一个在i层括号的'|'运算符
//l2[i] 代表目前最后一个在i层括号的'&'运算符
//c1[i] 代表目前和i同层的最后一个'|'运算符
//c2[i] 代表目前和i同层的最后一个'&'运算符
int cnt1 = 0, cnt2 = 0;

//cnt1和cnt2分别计算a&b 和 a|b 的“短路”的次数
int dfs(int l, int r) {
	//1|0或者1|1都是'|'短路
	if (c1[r] >= l) {
		int ans = dfs(l, c1[r] - 1);
		if (ans == 1) {
			++cnt1;
			return 1;
		}
		return (ans | dfs(c1[r] + 1, r));
	}
	//0&1或者0&0都是'&'短路
	if (c2[r] >= l) {
		int ans = dfs(l, c2[r] - 1);
		if (ans == 0) {
			++cnt2;
			return 0;
		}
		return (ans & dfs(c2[r] + 1, r));
	}
	////不在括号内的运算符不存在
	if (c[l] == '(' && c[r] == ')') {
		return dfs(l + 1, r - 1); //去除括号
	}
	return c[l] - '0';
}

int main () {
	ios::sync_with_stdio(0);
	cin.tie();
	cout.tie();
	cin>>c+1;
	int x = 0,len=strlen(c + 1);//strlen(c+1)调用超时?x表示当前括号有x层
	for (int i = 1; i <= len; i++) {
		if (c[i] == '(')
			x++;
		else if (c[i] == ')')
			x--;
		else if (c[i] == '|')
			l1[x] = i;
		else if (c[i] == '&')
			l2[x] = i;
		//最后一个在i这个位置前且与i同层的'|'运算符
		c1[i] = l1[x];
		//最后一个在i这个位置前且与i同层的'&'运算符
		c2[i] = l2[x];
	}
	int answer = dfs(1, len);
	cout<<answer<<'\n';
	cout<<cnt2<<" "<<cnt1;
	return 0;
}