4.21~4.27

CF1013 div3

给个题目链接,就不一题一题截图了:https://codeforces.com/contest/2091

F

比较好的思路:dp+前缀和

u[i][j]表示从i-1排满足距离条件的点到点(i,j)的所有走法之和,状态转移方程为:

u[i][j] = (prep[i-1][r2] - prep[i-1][l2]);

p[i][j]表示在第i排满足距离条件的点横着走到点(i,j)的所有走法之和,状态转移方程为:

p[1][j] = (preu[1][r1] - preu[1][l1]);

preu[i][j]表示第i排的u的前缀和

prep[i][j]表示第i排的p的前缀和

先特殊处理第1排(起始排),再从2排到n排逐排处理

比较我硬着头皮写的思路和官解的思路,我发现两个本质上差不多,就是实现方法不同

来看看我写的屎:
(dp+BFS)

#include <bits/stdc++.h>
using namespace std;
using pii = pair<int,int>;
const int maxn = 2e3+2;
const int mod = 998244353;

int dp[maxn][maxn],temp[maxn];
char room[maxn][maxn];
int n,m,d, explore[][2] = {0,1,0,-1,1,0,1,-1,1,1};
bool vis[maxn][maxn],vis2[maxn];

inline int dis(int x1,int y1,int x2,int y2){
	return ceil(sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)));
}
inline bool check(int x1,int y1,int x2,int y2,int d){
	return room[x2][y2] == 'X' && x2>=1 && x2<=n && y2>=1 && y2<=n && dis(x1,y1,x2,y2)<=d;
}

void bfs(int x,int y,int d){
	for(int i=1;i<=n;i++)
		for(int j=1;j<m;j++)
			vis[i][j] = 0;
	vector <int> vec[maxn];
	vis[x][y] = 1;
	queue <pii> q;
	q.push({x,y});
	int cnt = 0;
	while(!q.empty()){
		pii start = q.front();
		q.pop();
		pii next;
		for(int i=0;i<5;i++){
			next.first = start.first + explore[i][0];
			next.second = start.second + explore[i][1];
			if(check(x,y,next.first,next.second,d) && !vis[next.first][next.second] && x!=next.first){
				vec[next.first-x].push_back((dp[next.first][next.second])%mod);
				cnt = max(cnt,next.first-x);
				q.push({next.first,next.second});
				vis[next.first][next.second] = 1;
			}
		}
	}
	for(int i=0;i<=cnt;i++){
		int si = vec[i].size();
		sort(vec[i].begin(),vec[i].end(),greater<int>());
		//cout << "vec[" << i << "]:\n";
		//for(int j=0;j<si;j++){
		//	cout << vec[i][j] << ' ';
		//}
		//cout << '\n';
		for(int j=0;j<si && j<2;j++){
			dp[x][y] = (dp[x][y] + vec[i][j]) % mod;
		}
	}
	//cout << "-------\n";
	return;
}

void bfs2(int x,int y,int d){
	memset(vis2,0,sizeof(vis2));
	vis2[y] = 1;
	queue <int> q;
	q.push(y);
	while(!q.empty()){
		int start = q.front();
		q.pop();
		int next;
		for(int i=0;i<2;i++){
			next = start + explore[i][1];
			if(!vis2[next] && next>=1 && next<=m && room[x][next] == 'X' && dp[x][next] != 0 && abs(next-y)<=d){
				q.push(next);
				vis2[next] = 1;
				temp[y]++;
			}
		}
	}
	return;
}

void solve(){
	cin >> n >> m >> d;
	for(int i=1;i<=n;i++){
		memset(temp,0,sizeof(temp));
		string s;
		cin >> s;
		s = ' ' + s;
		if(i==n) for(int j=1;j<=m;j++){
			room[i][j] = s[j];
			dp[i][j] = 1;
		}
		else for(int j=1;j<=m;j++) room[i][j] = s[j];
	}
	for(int i=n;i>=1;i--){
		bool pd = 1;
		for(int j=1;j<=m;j++){
			if(room[i][j] == 'X'){
				bfs(i,j,d);
				pd = 0;
			}
		}
		if(pd){
			cout << "0\n";
			return;
		}
		for(int j=1;j<=m;j++){
			if(room[i][j]=='X') bfs2(i,j,d);
		}
		for(int j=1;j<=m;j++)
			if(room[i][j]=='X') dp[i][j]+=temp[j];
	}
	int ans = 0;
	for(int j=1;j<=m;j++) ans += dp[1][j];
	cout << ans << '\n';
}

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int t;
	cin >> t;
	while(t--){
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				dp[i][j] = 0;
		solve();
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++)
				cout << dp[i][j] << ' ';
			cout << '\n';
		} 
	}
	return 0;
}

官解是dp+前缀和,看了官解后我重新写了这道题的code,看看官解是多么的优美:

#include <bits/stdc++.h>
#define int long long
#define r1 min(j+d,m)
#define l1 max(j-d-1,0LL)
#define r2 min(j+d-1,m)
#define l2 max(j-d,0LL)
using namespace std;
const int mod = 998244353;
const int maxn = 2e3+2;

int t,n,m,d;
int u[maxn][maxn], p[maxn][maxn], preu[maxn][maxn],prep[maxn][maxn];
char room[maxn][maxn];

signed main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin >> t;
	while(t--){
		cin >> n >> m >> d;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				cin >> room[i][j];
		for(int i=0;i<=n;i++)
			for(int j=0;j<=m+1;j++)
				u[i][j] = p[i][j] = preu[i][j] = prep[i][j] = 0;
		for(int j=1;j<=m;j++)
			if(room[1][j] == 'X') u[1][j] = 1;
		for(int j=1;j<=m;j++)
			preu[1][j] = (preu[1][j-1] + u[1][j] +mod)%mod;
		for(int j=1;j<=m;j++)
			if(room[1][j] == 'X') p[1][j] = (preu[1][r1] - preu[1][l1] + mod)%mod;	
		for(int j=1;j<=m;j++)
			prep[1][j] = (prep[1][j-1] + p[1][j] + mod)%mod;
		for(int i=2;i<=n;i++){
			for(int j=1;j<=m;j++)
				if(room[i][j] == 'X') u[i][j] = (prep[i-1][r2] - prep[i-1][l2] + mod)%mod;
			for(int j=1;j<=m;j++)
				preu[i][j] = (preu[i][j-1] + u[i][j] + mod)%mod;
			for(int j=1;j<=m;j++)
				if(room[i][j] == 'X') p[i][j] = (preu[i][r1] - preu[i][l1] + mod)%mod;
			for(int j=1;j<=m;j++)
				prep[i][j] = (prep[i][j-1] + p[i][j] + mod)%mod;
		}
		cout << prep[n][m] << '\n';
	}
	return 0;
}

E

数论。

不必在意如何计算题目里的gcd,lcm,都是障眼法

先欧拉筛筛出2~1e7内的所有素数,再按规律进行处理

找规律的思路如下图所示

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e7+10;

bool vis[N];
vector <int> prime;

int eus(){
    int cnt = 0;
    for(int i=2;i<=N;i++){
        if(!vis[i]) { prime.push_back(i); ++cnt;}
        for(int j=0;j<cnt;j++){
            if(i*prime[j] > N) break;
            vis[i*prime[j]] = 1;
            if(i%prime[j] == 0) break;
        }
    }
    return cnt;
}

void solve(int& n,int& si){
    ll sum = 0;
    for(int i=0;i<si;i++){
        if(prime[i] > n) break;
        sum += ll(n/prime[i]);
    }
    cout << sum << '\n';
}

int main(void){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int si = eus();
    int t;
    cin >> t;
    while(t--){
        int n;
        cin >> n;
        solve(n,si);
    }
    return 0;
}

D

贪心。用二分求出最长长凳的最小长度

#include <bits/stdc++.h>
#define int long long
using namespace std;

signed main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int t;
	cin >> t;
	while(t--){
		int n,m,k;
		cin >> n >> m >> k;
		int l=1, r=1e9;
		while(l <= r){
			int mid = (l+r) >> 1;
			if(n*(m - m/mid) >= k) r = mid-1;
			else l = mid + 1;
			//cout<<"l:"<<l<<" r:"<<r<<'\n'; 
		}
		cout << l-1 << '\n';
	}
	return 0;
}

C

思维题,构造。自己写几组例子,注意到(注意力惊人):

当n为偶数时,不可能构造出来这样的序列;

当n为奇数时,按照1 3 5 ... n 2 4 6.... n-1这样的规律,先输出全部奇数,再输出全部偶数,就是一组构造

#include <bits/stdc++.h>
using namespace std;

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int t;
	cin >> t;
	while(t--){
		int n;
		cin >> n;
		if(n%2==0){
			cout << "-1\n";
			continue; 
		}
		for(int i=1;i<=n;i+=2) cout << i << ' ';
		for(int i=2;i<n;i+=2) cout << i << ' ';
		cout << '\n';
	}
	return 0;
}

B

贪心,对序列排序。为了让团队数量最多,当a[i] >=x时,就选它一个组成团队;当a[i] < x时,不断往前找,如果找到某a[index],使得a[index]*(i-index+1) < x,ans++。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+1;

int a[maxn];
int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int t;
	cin >> t;
	while(t--){
		int n,x;
		cin >> n >> x;
		for(int i=0;i<n;i++) cin >> a[i];
		sort(a,a+n);
		int ans = 0;
		for(int i=n-1;i>=0;i--){
			if(a[i] >= x) ans++;
			else{
				//cout << i << ' ';
				int index = i;
				bool pd = 1;
				while(a[index]*(i-index+1) < x){
					index--;
					if(index < 0){
						pd = 0;
						break;
					}
				}
				if(pd) ans++;
				i = index;
				//cout << i << '\n';
			}
		}
		cout << ans << '\n';
	}
	return 0;
}

A

贪心,开个vector模拟即可

#include <bits/stdc++.h>
using namespace std;

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int t;
	cin >> t;
	while(t--){
		int n;
		cin >> n;
		vector <int> a = {2,0,2,5,0,3,0,1};
		bool pd = 1;
		for(int i=1;i<=n;i++){
			int cache;
			cin >> cache;
			vector<int>::iterator it = find(a.begin(),a.end(),cache);
			if(it!=a.end()) a.erase(it);
			if(pd && a.empty()){
				cout << i << '\n';
				pd = 0;
			}
		}
		if(pd && !a.empty()) cout << "0\n";
	}
	return 0;
}

牛客周赛88

E

dp,有点像背包,但不太背包。总的来说不难,但是由于错误使用了memset,这题卡了n久。。。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 2e5+1;
const int inf = -1e16;
int a[maxn],b[maxn],dp[maxn][2];

signed main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n,k;
	cin >> n >> k;
	for(int i=1;i<=n;i++)
		cin >> a[i];
	for(int i=1;i<=n;i++)
		cin >> b[i];
	dp[0][1] = dp[0][0] = 0;
	dp[1][0] = a[1];dp[1][1] = inf;
	for(int i=2;i<=n;i++){
		if(dp[i-1][1] >= k && dp[i-1][1]-k+a[i] > dp[i-1][0]+a[i])
			dp[i][0] = dp[i-1][1] - k + a[i];
		else dp[i][0] = dp[i-1][0] + a[i];
		if(dp[i-1][0] >= k && dp[i-1][0]-k+b[i] > dp[i-1][1]+b[i])
			dp[i][1] = dp[i-1][0] - k + b[i];
		else dp[i][1] = dp[i-1][1] + b[i];
	}
	cout << max(dp[n][0],dp[n][1]) << '\n';
	return 0;
}

D

很简单,直接把x当作二进制,遍历每个数位,遇到0就能构造出y,没有0就输出-1

#include <bits/stdc++.h>
using namespace std;

int main(void){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        int x;
        cin >> x;
        int x1 = x, times = 0;
        while(1){
            //cout << x1 << '\n';
            if(x1 == 0){
                cout << "-1\n";
                break;
            }
            if(!(x1 & 1)){
                cout << pow(2,times) << '\n';
                break;
            }
            x1 = x1 >> 1;
            times++;
        }
    }
    return 0;
}

C

和B差不多,C题是只要某个点的x or y坐标和它所对的中点的x or y坐标相等即可

#include <bits/stdc++.h>
using namespace std;
double x[4], y[4], x_z[4], y_z[4];
 
void pd(){
    for(int i=1;i<=3;i++){
        if(x[i] == x_z[i]){
            cout << "YES\n";
            return;
        }
    }
    for(int i=1;i<=3;i++){
        if(y[i] == y_z[i]){
            cout << "YES\n";
            return;
        }
    }
    cout << "NO\n";
    return;
}
 
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int t;
    cin >> t;
    while(t--){
        for(int i=1;i<=3;i++)
            cin >> x[i] >> y[i];
        x_z[1] = (x[2] + x[3])/2.0;
        x_z[2] = (x[1] + x[3])/2.0;
        x_z[3] = (x[1] + x[2])/2.0;
        y_z[1] = (y[2] + y[3])/2.0;
        y_z[2] = (y[1] + y[3])/2.0;
        y_z[3] = (y[1] + y[2])/2.0;
        //for(int i=1;i<=3;i++) cout << x[i] << ' ' << y[i] << '\n';
        pd();
    }
    return 0;
}

B

很简单的题,只需要判断三角形三个顶点与三个中点,是否存在某个顶点的x or y坐标和某个中点x or y坐标同时为0即可

#include <bits/stdc++.h>
using namespace std;
double x[4], y[4], x_z[4], y_z[4];

void pd(){
	for(int i=1;i<=3;i++){
		if(x[i] == 0)
			for(int j=1;j<=3;j++)
				if(x_z[j] == 0){
					cout << "YES\n";
					return;
				}
		if(y[i] == 0)
			for(int j=1;j<=3;j++)
				if(y_z[j] == 0){
					cout << "YES\n";
					return;
				}
	}
	cout << "NO\n";
	return;
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int t;
	cin >> t;
	while(t--){
		for(int i=1;i<=3;i++)
			cin >> x[i] >> y[i];
		x_z[1] = (x[1] + x[2])/2.0;
		x_z[2] = (x[2] + x[3])/2.0;
		x_z[3] = (x[1] + x[3])/2.0;
		y_z[1] = (y[1] + y[2])/2.0;
		y_z[2] = (y[2] + y[3])/2.0;
		y_z[3] = (y[1] + y[3])/2.0;
		//for(int i=1;i<=3;i++) cout << x[i] << ' ' << y[i] << '\n';
		pd();
	}
	return 0;
}

A

签到题,略过

#include <bits/stdc++.h>
using namespace std;

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int a,b,c;
	cin >> a >> b >> c;
	if(a*b <= c) cout << "YES\n";
	else cout << "NO\n";
	return 0;
}

改版经典--导弹拦截

这道题二十多年了,比我都大......

这道最长不上升子序列 我去年做过最原始的版本,原始解法是dp,复杂度O(n2),今天重新写了dp的code,很简短简单:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;

int a[maxn], dp[maxn];

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int cnt = 0, ans = 0, sum = 1;
	while(cin >> a[++cnt]);cnt--;
	for(int i=1;i<=cnt;i++) dp[i] = 1;dp[0]=0;
	for(int i=1;i<=cnt;i++){
		for(int j=i+1;j<=cnt;j++){
			if(a[j] <= a[i]){
				dp[j] = max(dp[j],dp[i]+1);
				ans = max(ans,dp[j]);
			}
		}
	}
	cout << ans << '\n';
	return 0;
}

来看洛谷P1020

题还是那道题,但是数据加强了,老套的n2的dp过不了这道题

因此用O(nlogn)的二分来做

二分可以用C++标准模板库里的lower_bound()upper_bound()来实现

但要注意:二分只能实现找到最长上升/非降子序列(Lis/Lnds)的长度,但是子序列的内容不一定正确

找最长上升子序列的二分方法:

一个队列(推荐用vector),从小到大排队,

下一个数如果**>**队尾元素,那么就排到队尾。

下一个数如果**<=**队尾元素,那么就找到队内的第一个**>=**该元素的值,然后替换它

vector <int> vec;
vec.push_back(a[0]);
for(int i=1;i<=n;i++){
    if(a[i] > *vec.rbegin()) vec.push_back(a[i]);
    else *lower_bound(vec.begin(),vec.end(),a[i]) = a[i];
}

找最长非降子序列的二分方法:

一个队列(推荐用vector),从小到大排队,

下一个数如果**>=**队尾元素,那么就排到队尾。

下一个数如果**<**队尾元素,那么就找到队内的第一个**>**该元素的值,然后替换它

vector <int> vec;
vec.push_back(a[0]);
for(int i=1;i<=n;i++){
    if(a[i] >= *vec.rbegin()) vec.push_back(a[i]);
    else *upper_bound(vec.begin(),vec.end(),a[i]) = a[i];
}

本道题的二分code:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;

int a[maxn];
vector <int> lnis/*最长不升子序列*/, lis/*最长上升子序列*/;

int main(void){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n = 0;
	while(cin >> a[n++]);n--;
	lnis.push_back(a[0]);
	lis.push_back(a[0]);
	for(int i=1;i<n;i++){
		if(a[i] <= *lnis.rbegin()) lnis.push_back(a[i]);
		else *upper_bound(lnis.begin(),lnis.end(),a[i],greater<int>()) = a[i];
		if(a[i] > *lis.rbegin()) lis.push_back(a[i]);
		else *lower_bound(lis.begin(),lis.end(),a[i]) = a[i];
	}
	cout << lnis.size() << '\n' << lis.size() << '\n';
	return 0;
}
posted @ 2025-06-14 12:27  HLAIA  阅读(10)  评论(0)    收藏  举报