Codeforces Round 937 (Div. 4)

Codeforces Round 937 (Div. 4)

B题是输出规律图形题,对于这种题不能直接不思考就上去模拟,而应该思考一下数学规律,往往是模意义下的规律。

本题只需要模4以后的结果分为两类,分别讨论即可。对于模4可以利用位运算取出第二位的,这与模2同理。

char s1='#';
char s2='.';
void solve(){
	cin>>n;
	vector<vector<char>>ans(2*n+1,vector<char>(2*n+1,'0'));
	//vector<vector<bool>>v(2*n+1,vector<bool>(2*n+1,0));
	for(int i=1;i<=2*n;i++){
		for(int j=1;j<=2*n;j++){
			int u=i%4;int v=j%4;
			if(u==1||u==2){
				if(v==1||v==2){
					ans[i][j]=s1;
				}
			}
			else if(u==3||u==0){
				if(v==3||v==0){
					ans[i][j]=s1;
				}
				
			}
			if(ans[i][j]=='0')ans[i][j]=s2;
		}
	}
	for(int i=1;i<=2*n;i++){
		for(int j=1;j<=2*n;j++){
			cout<<ans[i][j];
		}
		cout<<endl;
	}
}

C:增加常识:12小时制没有0,是从1-12开始的,这符合时钟表盘下的数字。24小时制是没有24.

D:要求快速判断一个数能不能由只含若干0和1的数字相乘得到。

Solution:对于多次询问,我们提前打表预处理,实现\(O(1)\)查询。首先根据范围利用状态压缩得到范围内的可能01串,然后只需要将他们随意相乘能得到哪些数。

  • 我只想到了dfs暴搜,到边界了就剪枝return

  • 正解是跑完全背包,可达性统计

    Code:

    int a[N];
    map<int,int>mp;
    vector<int>v;
    void dfs(int u){
    	if(u>100000)return ;
    	//cerr<<u<<endl;
    	mp[u]=1;
    	for(int i=0;i<30;i++){
    		dfs(u*v[i]);
    	}
    }
    void init(){
    	mp[100000]=1;
    	
    	for(int i=1;i<32;i++){
    		int len=__lg(i);
    		string s;
    		for(int j=len;j>=0;j--){
    			if((i>>j)&1)s+="1";
    			else s+="0";
    		}
    		int num=stoi(s);
    		mp[num]=1;
    		//cerr<<num<<endl;
    		if(num!=1)v.push_back(num);
    		}
    		dfs(1);
    	//cerr<<v.size()<<endl;
    }
    void solve(){
    	cin>>n;
    	if(mp[n])cout<<"YES"<<endl;
    	else cout<<"NO"<<endl;
    }
    

jiangly的代码学习一下

#include <bits/stdc++.h>

using i64 = long long;

constexpr int N = 1E5;

int dp[N + 1];

void solve() {
    int n;
    std::cin >> n;
    
    std::cout << (dp[n] ? "YES" : "NO") << "\n";
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    std::vector<int> bin;
    for (int s = 1; s < (1 << 5); s++) {
        int x = 0;
        for (int i = 4; i >= 0; i--) {
            x = x * 10 + (s >> i & 1);
        }
        bin.push_back(x);
    }
    bin.push_back(N);
    
    dp[1] = 1;
    for (auto x : bin) {
        for (int y = 1; y * x <= N; y++) {
            dp[y * x] |= dp[y];
        }
    }
    
    int t;
    std::cin >> t;
    
    while (t--) {
        solve();
    }
    
    return 0;
}

E:按照题意暴力模拟即可,需要注意的是

  • 只有因子长度string才可能作为daan
  • 需要拿两个子串分别check一遍,答案一定才被覆盖
void solve(){
	cin>>n;
	string s;
	cin>>s;
	vector<int>v;
	for(int i=1;i*i<=n;i++){
		if(n%i==0){
			v.push_back(i);
			if(i*i!=n){
				v.push_back(n/i);
			}
		}
	}
	auto check=[&](string s1,int len){
		string tmp;
		for(int i=1;i<=n/len;i++)tmp+=s1;
		int cnt=0;
		for(int i=0;i<n;i++){
			if(tmp[i]!=s[i])cnt++;
		}
		
		//cerr<<len<<" "<<cnt<<endl;
		if(cnt<=1)return true;
		 return false;
	};
	sort(v.begin(),v.end());
	
	
	for(auto x:v){
		//cerr<<x<<" ";
		string s1=s.substr(0,x);
	if(x==n){
		cout<<x<<endl;
		return ;
	}
	string s2=s.substr(x,x);
		if(check(s1,x)||check(s2,x)){
			cout<<x<<endl;;
			return ;
		}
	}
}

F:给定出度为0,1,2的点的具体数量为\(c,b,a\).要求构造一棵树高最小的树

Solution:首先很容易想到贪心方案:先把所有的2用完,然后再用1,最后补0。

  • 对于无解情况的判断,利用出度和边的数量关系构造等式\(a+2b=a+b+c-1\),不符合这个的直接无解

  • 对于贪心的代码实现,非常的trick,利用bfs每次先把点的对应高度加进去,至于是哪种类型的点需要等到出队的时候再根据剩余情况分配。

    //2a+b=a+b+c-1
    void solve(){
    	int a,b,c;cin>>a>>b>>c;
    	if(a+1!=c){
    		cout<<-1<<endl;
    		return ;
    	}
    	queue<int>q;
    	int x=0;int ans=0;
    	q.push(0);
    	while(q.size()){
    		auto u=q.front();
    		//cerr<<u<<endl;
    		q.pop();
    		ans=u;
    		if(a){
    			a--;
    			q.push(u+1);
    			q.push(u+1);
    		}
    		else if(b){
    			b--;
    			q.push(u+1);
    		}
    	}
    	cout<<ans<<endl;
    }
    

G题: n个点,每个点有两种性质,两个点之间有一个性质相同就连边,图提前给定,求图上一条最长简单路径

Solution:对于这种一般图求最长路,在n范围较小的情况下,应该快速意识到是状态压缩的TSP类似问题。对于所有可能作为终点的点,计算他的最好状态中1的个数。

void solve(){
	vector<string>v1;
	vector<string>v2;
		cin>>n;
	vector<vector<int>>w(n+1,vector<int>(n+1,0));
	
	for(int i=0;i<n;i++){
		string s1,s2;cin>>s1>>s2;
		v1.push_back(s1);
		v2.push_back(s2);
	}
	for(int i=0;i<n;i++){
		for(int j=i;j<n;j++){
			//if(j==i)continue;
			if(v1[i]==v1[j]||v2[i]==v2[j]){
				w[i][j]=1;w[j][i]=1;
			}
		}
	}
	// for(int i=0;i<n;i++){
		// for(int j=0;j<n;j++){
			// cerr<<w[i][j];
		// }
		// cerr<<endl;
	// }
	//寻找答案状态:枚举最终的状态和终点,从而计算答案
	int ans=0;
	vector<vector<int>>dp((1<<n)+1,vector<int>(n+1,0));
	for(int i=0;i<n;i++)dp[1<<i][i]=1;
	for(int i=0;i<(1<<n);i++){
		for(int j=0;j<n;j++){
			int u=(i>>j)&1;
			if(u==0)continue;
			for(int k=0;k<n;k++){
				//为什么不能判断终点j和k转移重复
				int v=(i>>k)&1;
				if(v==0)continue;
				int last=i-(1<<j);
	dp[i][j]|=dp[last][k]&w[k][j];
//	cerr<<i<<" "<<j<<endl;
	if(dp[i][j]){
		ans=max(ans,__builtin_popcount(i));
	}
	
			}
		}
	}
	
	cout<<n-ans<<endl;
}
posted @ 2024-04-04 13:58  potential-star  阅读(16)  评论(0编辑  收藏  举报