暑假补题记 6
找最大的简单环
题解:首先就是如果有一条链的左右起点相同,那么起点链就要强制更换
然后就是一直递归的去跑,不断判断这个链可不可以当起点链,就是当它比前面跑的环都大的时候,那我们就把这个链当做新的起点链,否则一直跑环就可以了,最后一条链一般都直接加上即可因为最后一个环肯定可以跑完这一条链
if(abs(b[i+1]-c[i+1])>sum+a[i]-(abs(b[i+1]-c[i+1])+1)) sum=abs(b[i+1]-c[i+1])+1;
这里就是更新成新的起点链,注意前面的abs不可以加1因为无论如何他要成环经过你的链会用到两个或者一个点
#include <bits/stdc++.h> #pragma GCC optimize("Ofast") #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> //#define double long double #define int long long #define endl '\n'; using namespace std; const int N=1e5+7,M=1e1; const int INF = 0x3f3f3f3f; const int mod=998244353; typedef pair<int,int> PII; int a[N],b[N],c[N]; int32_t main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); int t; cin>>t; while (t--) { int n; cin >> n; for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= n; i++) cin >> b[i]; for (int i = 1; i <= n; i++) cin >> c[i]; int x = 0, y = 0; x = b[2], y = c[2]; int sum = abs(x - y) + 1; int ans = 0; for (int i = 2; i < n; i++) { if (b[i + 1] == c[i + 1]) { ans = max(ans, sum + a[i]); sum = 1; } else { ans=max(ans,sum+a[i]); if(abs(b[i+1]-c[i+1])>sum+a[i]-(abs(b[i+1]-c[i+1])+1)) sum=abs(b[i+1]-c[i+1])+1; else sum+=a[i]-abs(b[i+1]-c[i+1])+1; } } sum+=a[n]; ans=max(ans,sum); cout << ans << endl; } }
题意:搞一个排列,一个一个按顺序乘出来,然后减去最大两个向乘的值即可
题解:直接暴力去把后缀一个个反转判断反转几个是最大值。
#include <bits/stdc++.h> #pragma GCC optimize("Ofast") #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> //#define double long double #define int long long #define endl '\n'; using namespace std; const int N=1e5+7,M=1e1; const int INF = 0x3f3f3f3f; const int mod=998244353; typedef pair<int,int> PII; int a[N],b[N],c[N]; int32_t main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); int t; cin>>t; while (t--) { int n; cin>>n; int sum=0; for(int i=0;i<n;i++) { int ans=0,x=0,y=0; for(int j=1;j<=i;j++) { x+=j*j; ans=max(j*j,ans); } for(int k=n,z=i+1;z<=n;z++,k--) { y+=k*z; ans=max(k*z,ans); } sum=max(sum,x+y-ans); } cout<<sum<<endl; } }
题解:dp
首先我们可以推一下最优的摆放方式,可以得出第i个有人的位子上那么我们就放i之后的位置上尽量不往前放,可以证明是最优的摆放方法所以我们就可以来推转移方程了
首先dp[i]代表前i个有人的位子[j]代表j个空位
所以dp[i][j]代表时间
dp[0][j]=0;
如果前面没有人那就不需要换所以时间为0
然后就是状态转移
首先我们遍历前i个有人的空位,然后在遍历j个空位
dp[i][j]=min(dp[i][j-1](代表着前i个人已经不在自己原来的空位上,已经排好了,只需要上一个空位转移过来),dp[i-1][j-1]+abs(b[i]-c[i])(代表着第i个人转移到j的空位上所以坐标相减))我们取最小值就是答案了
#include <bits/stdc++.h> #pragma GCC optimize("Ofast") #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> //#define double long double #define int long long #define endl '\n'; using namespace std; const int N=5e3+8,M=1e1; const int INF = 0x3f3f3f3f; const int mod=998244353; typedef pair<int,int> PII; int a[N],b[N],c[N]; int dp[N][N]; int32_t main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); int n; cin>>n; int t=0,t1=0; for(int i=1;i<=n;i++) { cin>>a[i]; if(a[i]) b[++t]=i; else c[++t1]=i; } memset(dp,0x3f, sizeof (dp)); for(int j=0;j<=t1;j++) { dp[0][j]=0; } for(int i=1;i<=t;i++) { for(int j=i;j<=t1;j++) { dp[i][j]=min(dp[i][j-1],dp[i-1][j-1]+abs(b[i]-c[j])); } } cout<<dp[t][t1]<<endl; return 0; }
恶心死了
题解:这道题不要胡搞八搞,往简单了的想
无非m里面就是用1元硬币和k元硬币所以我们把两个分开
c1个k元的和c2个1元的这样子就好搞了
首先我们先搞一元的直接减如果负数那就是不够,那就绝对值加到要用的花色一元硬币,这个就是一定的
然后如果是正数,那我们看看它可不可以搞出k个一元硬币去补k元硬币,最后搞k元硬币直接减就行c1为正说明还需要k元硬币,反之不要
#include <bits/stdc++.h> #pragma GCC optimize("Ofast") #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> //#define double long double #define int long long //#define endl '\n'; using namespace std; const int N=1e5+8,M=1e1; const int INF = 0x3f3f3f3f; const int mod=998244353; typedef pair<int,int> PII; int32_t main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); int t; cin>>t; while (t--) { int m, k, a1, ak; cin >> m >> k >> a1 >> ak; int c1=m/k,c2=m%k; a1-=c2; int ans=0; if(a1<0) { ans+=abs(a1); } else { c1-=(a1/k); } c1-=ak; if(c1<=0) { cout<<ans<<endl; } else { cout<<ans+c1<<endl; } } return 0; }
题解:一道类似dp的题
首先我们先考虑爱丽丝什么时候输
第一就是选完i点的时候,前面只有一个可以选了那么爱丽丝就数了
第二如果选的i点前面有小于i点的必胜点那么爱丽丝也输
所谓必胜点就是,dp跑出来的点就是之前统计最个点爱丽丝必赢的点,然后boo选了所以爱丽丝就输了
#include <bits/stdc++.h> #pragma GCC optimize("Ofast") #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> //#define double long double #define int long long //#define endl '\n'; using namespace std; const int N=3e5+8,M=1e1; const int INF = 0x3f3f3f3f; const int mod=998244353; typedef pair<int,int> PII; int p[N]; set<int> a,b; int32_t main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); int t; cin>>t; while (t--) { int n; cin>>n; for(int i=1;i<=n;i++) { cin>>p[i]; } if (n == 1){ cout << 0 << endl; continue; } int ans=0; b.insert(p[1]); for(int i=2;i<=n;i++) { if(a.lower_bound(p[i])==a.begin() && b.lower_bound(p[i])!= b.begin()) { ans++; a.insert(p[i]); } b.insert(p[i]); } cout<<ans<<endl; a.clear(); b.clear(); } return 0; }
这道题就是一个暴力枚举每一个状态看看到不到k次
首先一个for循环每一个元素最大解然后二分a[i]到a[i]+k就是这个得到的二分结果就是这个位子最优解,这个元素最优解之后后面的元素都要比他小1便利然后小一这个大于原来的元素就加小于或者等于就停止
然后判断是否大于k就好了
#include <bits/stdc++.h> #pragma GCC optimize("Ofast") #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> //#define double long double #define int long long //#define endl '\n'; using namespace std; const int N=1e5+8,M=1e1; const int INF = 0x3f3f3f3f; const int mod=998244353; typedef pair<int,int> PII; int n,k; int a[N]; int b[N]; bool check(int x,int y) { b[y]=x; for(int i=y+1;i<=n;i++) { b[i]=b[i-1]-1; } int an=0; for(int i=y;i<=n;i++) { if(a[i]>=b[i]) { if(an<=k) return true; else return false; } an+=b[i]-a[i]; } return false; } int32_t main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); int t; cin>>t; while (t--) { cin>>n>>k; int ans=0; for(int i=1;i<=n;i++) { cin>>a[i]; ans=max(ans,a[i]); } for(int i=1;i<=n;i++) { int l=a[i],r=a[i]+k; while (l<=r) { int mid=(l+r)/2; if(check(mid,i)) { ans=max(mid,ans); l=mid+1; } else r=mid-1; } } cout<<ans<<endl; } return 0; }
题意:就是两个数字可以组成一个类型的冰激淋,就是1和2和3就有12 13 23 这三种类型,然后给你一个数字表示类型种数,问你要多少个冰激淋球可以表示出来
题解:一看一点像排列组合就是C几那种 首先我们想类型为6最优解就是4就是4个不同的冰激淋球就可以,然后呢我们可以想到就是需要几个不同的就是最优解,但是有可能这几个不同的最优解无法组成那个数字
所以我们就需要几个相同的
我们先二分找出那几个不同的最优解,这个解组成的类型一定小于答案,然后在和答案相减余下的就是需要相同的数量
和二分答案相加即可
#include <bits/stdc++.h> #pragma GCC optimize("Ofast") #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> //#define double long double #define int long long //#define endl '\n'; using namespace std; const int N=1e5+8,M=1e1; const int INF = 0x3f3f3f3f; const int mod=998244353; typedef pair<int,int> PII; int32_t main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); int t; cin>>t; while (t--) { int n; cin>>n; int l,r; l=1,r=1e10+9; while (l<r) { int mid=(l+r)/2; if(mid*(mid-1)>=n*2) r=mid; else l=mid+1; } // cout<<l<<endl; while (l*(l-1)>n*2) { l--; } int dum=l*(l-1); l+=(n*2-dum)/2; cout<<l<<endl; } return 0; }
题意:给你一个d常数和电影院的电影个数和你可以去的次数,然后就是每一次电影的娱乐值
你去一次就是娱乐值-d*(这一次的下标-上一次的下标)
问你在m次下,最优解
题解:首先我们可以证明就是不管你怎么去d*i都只和最后一次有关
就是如果你最后一次去是下标为5那么就是 前面的娱乐值相加-d*5就是最终结果
所以我们直接一个个正数加如果满了m次就丢掉娱乐值最小的那次在加上当前娱乐值
可以用一个优先队列实现,录入一次排序一次然后在减去d*i比较MAX即可
#include <bits/stdc++.h> #pragma GCC optimize("Ofast") #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> //#define double long double #define int long long //#define endl '\n'; using namespace std; const int N=2e5+8,M=1e1; const int INF = 0x3f3f3f3f; const int mod=998244353; typedef pair<int,int> PII; int a[N]; priority_queue<int,vector<int>,greater<int>> q; int32_t main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); int t; cin>>t; while (t--) { int n,m,d; cin>>n>>m>>d; for(int i=1;i<=n;i++) { cin>>a[i]; } int ans=0,sum=0; int k=0; while (!q.empty()) q.pop(); for(int i=1;i<=n;i++) { if(a[i]>0 && k<m) { k++; q.push(a[i]); ans+=a[i]; } else if(k==m) { k--; ans-=q.top(); q.pop(); ans+=a[i]; q.push(a[i]); k++; } sum=max(sum,ans-d*i); } cout<<sum<<endl; } return 0; }
题意:给你一个数字,你有一种操作,就是减去当前数的一个约数,同一个约数最多只能被使用两次,你要用n次操作让这个数变成1即可
题解:这道题其实可以一步步慢慢推,首先这个数如果是偶数那么就可以看看这个数是不是2次幂的倍数,如果是那你就砍一半即可,不是就减去其2的最高次幂
因为当你减去他这个2的最高次幂,后面不会在出现这个2的最高次幂,因为你减去这个最高次幂不会影响整体的最高次幂
所以我们搞3种
x是基数:减一即可
x:2次幂的倍数,那这个数砍一半
x:可以被2除,但不是2次幂的倍数,直接减去2的最高次幂即可
#include <bits/stdc++.h> #pragma GCC optimize("Ofast") #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> //#define double long double #define int long long //#define endl '\n'; using namespace std; const int N=1e5+8,M=1e1; const int INF = 0x3f3f3f3f; const int mod=998244353; typedef pair<int,int> PII; int32_t main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); int t; cin>>t; while (t--) { int x; cin>>x; vector<int> a; a.push_back(x); while (x>1) { if(x&1) { a.push_back(x-1); x--; continue; } int ro=1; while (x%ro==0) { ro*=2; } ro/=2; if(x==ro) { a.push_back(x-ro/2); x-=ro/2; } else { a.push_back(x-ro); x-=ro; } } cout<<a.size()<<endl; for(auto i : a) { cout<<i<<' '; } cout<<endl; a.clear(); } return 0; }
题意:给你两种骨牌,可以竖着放可以横着放,都占2格,让你在给出的骨牌放置中染色,一个骨牌两边只能一般染白一边黑
问你,可不可以染成某一行黑和白数量相等,某一列也是如此。
题解:很简单,首先统计每一行有骨牌的个数,和每一列有骨牌的个数,然后发现某一行或者列有基数个直接输出-1即可
否则,我们两两找,因为横着的骨牌不会影响行黑白数所以考虑列就行
如果这一列有骨牌,往行下找,如果可以两两对应就可以搞出来了,就是这一行横着的放黑白,下一行就反过来放白黑,两两对应就够数量了
#include <bits/stdc++.h> #pragma GCC optimize("Ofast") #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> //#define double long double #define int long long //#define endl '\n'; using namespace std; const int N=600,M=1e1; const int INF = 0x3f3f3f3f; const int mod=998244353; typedef pair<int,int> PII; char c[500][500]; int32_t main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); int t; cin>>t; while (t--) { int n,m; cin>>n>>m; char a[n][m]; vector<int> h[N]; vector<int> l[N]; for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { c[i][j]='.'; } } for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { cin>>a[i][j]; } } int f=0; for(int i=0;i<n;i++) { int res=0; for(int j=0;j<m;j++) { if(a[i][j]!='.') res++; if(a[i][j]=='L') { l[j].push_back(i); } if(a[i][j]=='U') { h[i].push_back(j); } } if(res%2) { f=1; break; } } for(int i=0;i<m;i++) { int res=0; for(int j=0;j<n;j++) { if(a[j][i]!='.') res++; } if(res%2) { f=1; break; } } if(f) { cout<<-1<<endl; } else { for(int i=0;i<m;i++) { int x=l[i].size(); if(x==0) continue; int y=0; for(int j=0;j<x;j++) { int k=l[i][j]; if(y==0) { y=1; c[k][i]='W'; c[k][i+1]='B'; } else { y=0; c[k][i]='B'; c[k][i+1]='W'; } } } for(int i=0;i<n;i++) { int x=h[i].size(); if(x==0) continue; int y=0; for(int j=0;j<x;j++) { int k=h[i][j]; if(y==0) { y=1; c[i][k]='W'; c[i+1][k]='B'; } else { y=0; c[i][k]='B'; c[i+1][k]='W'; } } } for(int i=0;i<n;i++) { for(int j=0;j<m;j++) { cout<<c[i][j]; } cout<<endl; } } } return 0; }