2020 ICPC Shanghai Site重现

G. Fibonacci

题目大意:
f n f_n fn为斐波那契数列, f 1 = 1 , f 2 = 1 , f n = f n − 2 + f n − 1 f_1=1,f_2=1,f_n=f_{n-2}+f_{n-1} f1=1,f2=1,fn=fn2+fn1
计算 ∑ i = 1 n ∑ j = i + 1 n g ( f i , f j ) \sum_{i=1}^n\sum_{j=i+1}^ng(f_i,f_j) i=1nj=i+1ng(fi,fj)
其中,若 x ⋅ y x\cdot y xy为偶数, g ( x , y ) = 1 g(x,y)=1 g(x,y)=1,否则 g ( x , y ) = 0 g(x,y)=0 g(x,y)=0

思路:
观察斐波那契数列,可以发现:
在奇偶性上,该数列按“奇数、奇数、偶数”的顺序循环排列
每三个数字按奇偶性一循环。
由于 g ( x ) g(x) g(x)只在自变量为偶数时,值为1,所以只要关注偶数就好。
给定n个斐波那契数,则偶数个数为 k = n / 3 k=n/3 k=n/3,奇数个数为n-k。
在n个数字中任意选择两个不同的数字,其乘积为偶数,有以下两种情况:
1、偶数 × \times ×偶数: k × ( n − k ) k\times(n-k) k×(nk)
2、偶数 × \times ×奇数: C k 2 C_k^2 Ck2

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn=1e3+10;
const ll mod=998244353;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;
typedef unsigned long long ull;
inline ll lowbit(ll x){return x&(-x);} 
ll C(ll n,ll m){
	if(m>n) return 0;
	ll fz=1,fm=1;
	for(int i=n-m+1;i<=n;i++) fz*=i;
	for(int i=1;i<=m;i++) fm*=i;
	return fz/fm;
}
int main(){
	ll n;
	scanf("%lld",&n);
	ll k=n/3;
	ll ans=k*(n-k);
	ans+=C(k,2);
	printf("%lld\n",ans);
	return 0;
} 

M. Gitignore

题目大意:
给出n个要删除的文件路径,同时给出m个不能删除的文件路径。每次删除都只能删除一个文件或者一个文件夹(包括该文件夹内的所有内容),问最少需要删除多少个路径。

思路:
1、题目问最少要删除多少个路径,最省事的删法当然是整个文件夹一起删除(如果可以的话),所以我们其实应该先考虑哪些不能直接删除的文件路径,对于其他的文件,直接把文件夹删了就行,不过要注意不要重复删除(同一个文件夹只删一次)
2、对于不能删除的路径:
对其每一条子路径都要进行标记,都不能删除

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn=1e4+10;
const ll mod=998244353;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;
typedef unsigned long long ull;
inline ll lowbit(ll x){return x&(-x);} 
map<string,int>mp;
int main(){
	int t;
	cin>>t;
	while(t--){
		int n,m;
		string s[maxn];
		cin>>n>>m;
		mp.clear();//注意多组样例,每次清空
		int sum=n;
		for(int i=0;i<n;i++){
			cin>>s[i];
		}
		for(int i=0;i<m;i++){
			string a;
			cin>>a;
			string tmp="";
			for(int j=0;j<a.length();j++){
				tmp=a[j]+tmp;//所有子路径都需标记,都不能删除
				if(a[j]=='/'){
				//	cout<<"tmp: "<<tmp<<endl;
					mp[tmp]=1;
				}
			}	
		}
		for(int i=0;i<n;i++){
			s[i]+="/";
			string tmp="";
			for(int j=0;j<s[i].length();j++){
				tmp=s[i][j]+tmp;
				if(s[i][j]=='/'){
				//	cout<<"tmp: "<<tmp<<endl;
					if(mp[tmp]==0){//如果当前路径并未被标记过,说明该路径可以直接删除
						mp[tmp]=2;//标记为2:可以删除
					}
					else if(mp[tmp]==2){//当前路径可以直接删除
						sum--;
						break;//后面的文件路径无需再管
					}
				}
			}	
		}
		cout<<sum<<endl;
	} 
	return 0;
} 

B. Mine Sweeper II

题目大意:
扫雷游戏,"X"表示雷区, “.” 表示无雷区。每个无雷区上都有一个数字,记录该位置周围8个方向地雷的个数。现在给出A和B两张地图,它们的无雷区数字之和分别为sumA和sumB,求能否在n*m/2次以内改变B图,使得sumA=sumB。如果可以,输出修改之后的地图B,否则输出-1。

思路:
对于同一幅地图,将其中的雷区和无雷区位置对调,所得的无雷区数字之和是不变的!
题目给了nm/2次的限制,很容易想到的是:
如果A和B,相应位置,状态不同(此处状态指的是雷区、非雷区)的个数小于等于该值,则直接把B改成跟A一模一样,也不会超出次数。
但是如果大于该值,那就需要逆向思维,把A图所有位置的状态逆转(雷区变为非雷区,非雷区变为雷区),那么此时A和B位置相同但状态不同的数量一定是小于n
m/2的!!!然后把B修改成A逆转之和的状态就可以了。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn=1e3+10;
const ll mod=998244353;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;
typedef unsigned long long ull;
inline ll lowbit(ll x){return x&(-x);} 
char a[maxn][maxn],b[maxn][maxn];
int main(){
	int n,m;
	cin>>n>>m;
	int mmax=(m*n)/2;
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			cin>>a[i][j];
		}
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			cin>>b[i][j];
		}
	}
	int cnt=0;
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			if(a[i][j]!=b[i][j]) cnt++;
		}
	}
	if(cnt<=mmax){
		for(int i=0;i<n;i++){
			for(int j=0;j<m;j++){
				cout<<a[i][j];
			}
			cout<<endl;
		}
	}
	else{
		for(int i=0;i<n;i++){
			for(int j=0;j<m;j++){
				if(a[i][j]=='.') cout<<"X";
				else cout<<".";
			}
			cout<<endl;
		}
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值