ICPC 新疆省赛2025

传送门

按照开题顺序写一写

A

范围很小可以直接暴力,然后用set存下不同的a*b的值,输出set的size即可

点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

void solve() {
	ll a, b, x, y, p;
	set<ll> s;
	cin >> a >> b >> x >> y >> p;
	for (ll i = 0; i < 101; i++) {
		for (ll j = 0; j < 101; j++) {
			if (i * x + j * y <= p) {
				s.insert((a + i) * (b + j));
			}
		}
	} 
	cout << s.size();
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	ll _ = 1;
//	cin >> _;
	while (_--) {
		solve();
	}
	return 0;
}
 

H

每个超市有不同种类的鸡蛋,需要找出最少多少个超市(总共m个超市)可以提供全部种类(n个种类)的鸡蛋

由于数据范围比较小,一个超市的不同种类的鸡蛋可以用二进制来表示

1001就表示有1,4这两种鸡蛋

而且由于数据范围较小

所以只需要暴力搜索,每次选择一个超市就用二进制串与它异或,直到一个二进制串1的数量为n(用bitset实现更方便)

注意从第i家超市搜索就不考虑前i家超市,因为在之前的遍历中,已经考虑过了

点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll n, m;
bitset<21>s[22];
bitset<21>x;
int ans=INT_MAX;
bool book[22];

void dfs(int tot,int st)
{
	if(tot>m) return ;
	if(x.count()==n ){
		ans=min(ans,tot);
		return ;
	}
	bitset<21>xb=x;
	for(int i=st;i<=m;++i){
		x|=s[i];
		dfs(tot+1,i+1);
		x=xb;
	}
	return ;
	
}
void solve() {
	cin >> n >> m;
	for (ll i = 1; i <= m; i++) {
		ll k;
		cin>>k;
		for(int j=1;j<=k;j++){
			int num;cin>>num;
			s[i][num]=1;
            book[num]=1;
		}		
	}
    for(int i=1;i<=n;++i){
        if(!book[i]){
            cout<<-1<<endl;
            return ;
        } 
    }
	dfs(0,1);
	cout<<ans<<endl;
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	ll _ = 1;
//	cin >> _;
	while (_--) {
		solve();
	}
	return 0;
}
 

数据强化版本

[蓝桥杯 2019 省 A] 糖果

这个题的n达到了100,那么暴搜必然超时,所以得用上状压dp

定义dp[i]为i状态下最小购买多少包,初始化每一包糖代表的状态为1

之后从初始状态枚举到末状态,状态转移,而状态转移的途径由题目易得,一定要通过各个点或得到新节点

点击查看代码
#include<bits/stdc++.h>

using namespace std;

int n,m,k;
const int maxn=20;
int dp[1<<20],v[1<<20];

int main(){
	cin>>n>>m>>k;
	memset(dp,0x3f3f3f3f,sizeof(dp));
	for(int i=1;i<=n;++i) {
		int h=0,p;
		for(int j=1;j<=k;++j){
			cin>>p; --p;
			h=h|(1<<p);
		}
		dp[h]=1;
		v[i]=h;
	}
	for(int i=0;i<(1<<m);++i){//从初始状态枚举到末状态
		for(int j=1;j<=n;++j){
			dp[i|v[j]]=min(dp[i|v[j]],dp[i]+1);
		}
	} 
	if(dp[(1<<m)-1]==0x3f3f3f3f) cout<<-1<<endl;//得到
	else cout<<dp[(1<<m)-1];//到末状态的最小方案
	return 0;
}

E

暴力显然超时,明显要推出一个结论

求递推公式

$ f(a)=lcm(a,b)+gcd(a,b) $

$ f(a)=\frac{a*b}{gcd(a,b)}+gcd(a,b)$

$ f^2(a)=\frac{f(a)*b}{gcd(f(a),b)}+gcd(f(a),b)$

我们考虑
\(gcd(f(a),b)=gcd(\frac{a*b}{gcd(a,b)}+gcd(a,b),b)=gcd(b,(\frac{a*b}{gcd(a,b)}+gcd(a,b)) \mod b )=gcd(b,gcd(a,b))=gcd(a,b)\)

所以

$ f^2(a)=\frac{f(a)*b}{gcd(a,b)}+gcd(a,b)$

同理可得
$ f^{n} (a)=\frac{f^{n-1} (a)*b}{gcd(a,b)}+gcd(a,b)$

点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = unsigned long long;

void solve() {
    ll a, b, n, mod = 1e9 + 7;
    cin >> a >> b >> n;
    ll t=gcd(a, b);
    ll ans=(a*b/t%mod+t%mod)%mod;
    for (int i = 1; i < n; i++) {
         ans=(ans*b/t%mod+t%mod)%mod;
    
	}
	cout << ans %mod<<endl;
}        

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	ll _ = 1;
//	cin >> _;
	while (_--) {
		solve();
	}
	return 0;
}

F

此题贪心,找出数值为i的点,找出到每一个i+1的最小值,然后逐步求解到值m的最小距离

点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll n, m;
#define x first
#define y second 

const int maxn=3e3+10;
vector<pair<ll,ll> >a[maxn];
ll dis[maxn][maxn];
bool book[maxn][maxn];
void solve() {
	cin >> n >> m;
	for(int i=1;i<=n;++i){

		for(int j=1;j<=n;++j){
	   		dis[i][j]=1e9;
	   }
    }
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			int x;cin>>x;
			a[x].push_back({i,j});
			if(x==1) dis[i][j]=0;
			if(x==m) book[i][j]=1;
		}
	}
	for(int i=1;i<m;++i){
		for(auto j:a[i]){
			auto u=j;
			for(auto k:a[i+1]){
				auto v=k;
				dis[v.x][v.y]=min(dis[v.x][v.y],dis[u.x][u.y]+abs(u.x-v.x)+abs(u.y-v.y));
			}
		}
	} 
	
	ll ans=1e9;
	for(int i=1;i<=n;++i){

		for(int j=1;j<=n;++j){
			if(book[i][j]) ans=min(ans,dis[i][j]);
		}
	}
	if(ans==1e9)cout<<-1<<endl;
	else cout<<ans<<endl;
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	ll _ = 1;
//	cin >> _;
	while (_--) {
		solve();
	}
	return 0;
}
 

G

队友开的题,貌似很简单
给出每个字母的权值,字符串的权值是各个字母的乘积,W是子串中最大的乘积,找出权值为W的子串最长的长度

删除权值为0的字母即可

点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
	vector<ll> a(26), b(26, 0);
	for (ll i = 0; i < 26; i++) {
		cin >> a[i];
	}
	string s;
	cin >> s;
	for (ll i = 0; i < s.size(); i++) {
		b[s[i] - 'a']++;
	}
	ll ans = s.size();
    for (ll i = 0; i < 26; i++) {
        if (a[i] == 0) {
            ans -= b[i];
        }
    }
    if (ans == 0) {
        ans = s.size();
    }
	cout << ans << "\n";
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	ll _ = 1;
	cin >> _;
	while (_--) {
		solve();
	}
	return 0;
}
 

D

按照它的规则简化分式,是否能达到给定简化后的分式

又是队友开的题

比较化简前后消失的字符是否相同就可以

点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = unsigned long long;

void solve() {
    ll p, q;
    string a, b;
    cin >> a >> b;
    string c, d, e, f;
    vector<ll> g(10, 0), h(10, 0);
    for (p = 0; p < a.size(); p++) {
        if (a[p] == '/') {
            break;
        }
    }
    for (q = 0; q < b.size(); q++) {
        if (b[q] == '/') {
            break;
        }
    }
    for (ll i = 0; i < p; i++) {
        c += a[i];
    }
    for (ll i = p + 1; i < a.size(); i++) {
        d += a[i];
    }
    for (ll i = 0; i < q; i++) {
        e += b[i];
    }
    for (ll i = q + 1; i < b.size(); i++) {
        f += b[i];
    }
    ll j = 0;
    for (ll i = 0; i < c.size(); i++) {
        if (j < e.size() && c[i] == e[j]) {
            j++;
        } else {
            g[c[i] - '0']++;
        }
    }
    if (j != e.size()) {
        cout << "No\n";
        return;
    }
    j = 0;
    for (ll i = 0; i < d.size(); i++) {
        if (j < f.size() && d[i] == f[j]) {
            j++;
        } else {
            h[d[i] - '0']++;
        }
    }
    if (j != f.size() || g != h) {
        cout << "No\n";
        return;
    }
    cout << "Yes\n";
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	ll _ = 1;
	cin >> _;
	while (_--) {
		solve();
	}
	return 0;
}

补题

参考题解

B

看题和题解的时候注意一下,坐标是正常的笛卡尔坐标系,不是数组的排列方式

点击查看代码
#include<bits/stdc++.h>

using namespace std;
using ll=long long ;
const int mod=1e9+7;
int t;
ll qpow(ll a,ll b){
    ll cnt=1;
    while(b){
        if(b&1) cnt=cnt*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return cnt;
}
void solve(){
    ll n,m,k;
    cin>>n>>m>>k;
    if(k==1) cout<<1<<endl;
    else {
        ll t=(qpow(k-1,m+1)-1)*qpow(k-2,mod-2)%mod;
        if(k==2){
            t=m+1;
        }
        t=qpow(t,n-1)*qpow(k,m)%mod;
        cout<<t<<endl;
    }
}
int main(){
    cin>>t;
    while(t--){
        solve();
    }return 0;
}

C

涉及到二进制,那么此题就在暗示我们要从二进制的角度思考
rimage

image

原作者

点击查看代码
#include<bits/stdc++.h>
using namespace std;
 
const int mod = 1e9 + 7;  // 定义模数,用于结果取模 
 
int n, m;  // n: 城市数量, m: 二进制位数(固定为30,因为A_i < 2^30)
int x;     // 临时变量,存储当前城市的A_i值 
int f[33]; // f[j]表示二进制第j位上的路径数(0<=j<30)
int t;     // 测试用例数量 
 
void solve() {
    cin >> n;  // 读取当前测试用例的城市数量 
    m = 30;    // 设置二进制位数为30(因为A_i < 2^30)
    memset(f, 0, sizeof(f));  // 初始化f数组为0 
 
    for(int i = 1; i <= n; ++i) {  // 遍历每个城市 
        int v = 0;  // 存储从城市1到当前城市i的路径数 
        cin >> x;    // 读取当前城市的A_i值 
 
        // 计算路径数v:遍历所有二进制位 
        for(int j = 0; j < m; ++j) {
            if((x >> j) & 1) {  // 如果当前位是1 
                v = (v + f[j]) % mod;  // 累加该位上的路径数 
            }
        }
 
        // 如果是第一个城市,路径数为1(起点)
        v = (i == 1) ? 1 : v % mod; 
 
        // 如果是最后一个城市,输出结果 
        if(i == n) {
            cout << v << endl;
        }
 
        // 更新f数组:将当前路径数v贡献到所有为1的二进制位上 
        for(int j = 0; j < m; ++j) {
            if((x >> j) & 1) {
                f[j] = (f[j] + v) % mod;
            }
        }
    }
    return;
}
 
int main() {
    cin >> t;  // 读取测试用例数量 
    while(t--) {
        solve();  // 处理每个测试用例 
    }
    return 0;
}

赛后

喜提

posted @ 2025-05-20 17:22  归游  阅读(77)  评论(0)    收藏  举报