2021年中国大学生程序设计大赛(哈尔滨)Gym 103447
B Magical Subsequence DP
题意:
给定一个序列 A,选择一个长为偶数的子序列 B,使得 B1+B2=B3+B4...,问这个满足条件的子序列最长能是多少。
代码
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define int long long
const int N=1e5+10;
int a[N];
vector<int> vec[220];
int dp[N][220][2];//dp[i][j]表示在前i个数中选择,对第i个数进行操作,每两个数加起来为j
signed main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
vec[a[i]].push_back(i);
}
int maxx=2;
for(int i=1;i<=n;i++)
{
for(int j=2;j<=200;j++)
{
dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1]);
int k=j-a[i];
if(k<=0) continue;
if(vec[k].size()==0) continue;
int pos=upper_bound(vec[k].begin(),vec[k].end(),i)-vec[k].begin();//找到第一个大于i的位置
if(pos==vec[k].size()) continue;//没有这样的位置
int p=vec[k][pos];
dp[p][j][1]=max(dp[p][j][1],dp[i][j][0]+2);
}
}
for(int i=1;i<=n;i++)
{
for(int j=2;j<=200;j++)
{
maxx=max(dp[i][j][0],maxx);
maxx=max(dp[i][j][1],maxx);
}
}
cout<<maxx<<endl;
return 0;
}
E Power and Modulo
题意:
给你一个序列,第i个的值是$ 2^{i-1}\ mod M\(,问你是否存在一个唯一的M使得该序列成立。
**思路**
找到第一个\)a_{i}!= a_{i-1}*2a$的位置,则 \(M=2*a_{i-1}-a_i\),如果没有这样的位置就无解。然后从前往后暴力判断一下是否符合即可,记得特判一下首项是否为0.
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
const int N =1e5 + 10;
// const int M=1000000007;
int n,m;
// vector<int> ans;
int a[N];
int num[N];
void init(){
num[0] = 1;
for(int i = 1; i <= 35; i++){
// if(num[i - 1] * 2 > 1e9) break;
num[i] = num[i - 1] * 2;
}
// cout << num[33] << endl;
}
void slove()
{
//init();
//cout <<"?" << num[64] << endl;
int s = 0;
map<int, int> mp;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
if(a[i] == 0) s ++;
}
if(s == n){
cout << 1 << endl;
return;
}
bool flag = false;
int mod = -1;
int j = 0;
int u = 1;
for(int i = 1; i <= n; i++){
if(flag){
//if(mp.find(a[i]) == mp.end())
if(u * 2 % mod != a[i]){
cout << -1 << endl;
//continue;
return;
}
u = u * 2 % mod;
}
else{
if(num[i - 1] / 2 <= a[i] && a[i] != num[i - 1]){
cout << -1 << endl;
return;
}
//if(mp.find())
if(a[i] != num[i - 1]){
mod = num[i - 1] - a[i];
flag = true;
//j = i - 1;
u = num[i - 1];
}
}
}
if(mod == -1) cout << -1 << endl;
else cout << mod << endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t = 1;
init();
cin >> t;
while (t--)
slove();
return 0;
}
I Power and Modulo 二进制,思维,二分
题意
给你一个序列 \(A_n\),你可以进行多次操作;
每次操作可以选择一个任意长度的序列 \(B_m\) ,然后使得\(A_{B_i}-2^{i-1}\)。问你最少多少次操作可以将\(A_n\)序列变为零。
题解
可以发现
将A_n$ 分别求出来每个数的二进制位为 1 的数量
每次操作等价为消除下标从0开始的连续的1。
因此,问题等价为至少多少次操作将这些二进制的1消除掉。
问题也等价为:
将二进制存起来,从高位向低位变成一个不下降序列,此时最低位次数即所需次数
注意: 而为了使得操作次数最少,就要二进制数1的个数最少。
思路1:暴力,复杂度不明
从后往前遍历,如果发现当前位置大于前一个位置了,就匀到前一个位置些(匀出的个数*=2),以保证前一个位置个数 >= 当前位置个数。
进行若干遍此操作,便能保证低位到高位递减,同时 1 的个数最小化。
思路2:二分,复杂度明了
假设x次操作成立的话那么x+1次操作也一定成立,因此发现具有二段性可以二分。
和上面一样,我们从高位到低位枚举要将当前这一位的1全删掉需要的操作数(看看是否可以连续删即可),然后将当前这一位操作后剩下的给前面加上,再每一位都删除相应的数量即可,最后每一次的操作数累加起来就是res。
代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
ll ay[40];
int n;
bool check(ll x){
if(ay[0]>x) return 0;
ll a[40];
for(int i=0;i<=31;i++){
a[i]=ay[i];
}
a[32]=0;
for(int i=0;i<=31;i++){
if(i&&a[i]>a[i-1]||a[i]>x) return false;
ll y = i?a[i-1]-a[i]:x-a[i];
a[i+1]-=y/2;
a[i]+=y/2*2;
}
return true;
}
ll find()
{
ll l=0 , r=1e16;
while (l < r)
{
ll mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
return l;
}
int main()
{
int _;scanf("%d",&_);
while(_--){
memset(ay,0,sizeof ay);
scanf("%d",&n);
for(int i=1;i<=n;i++){
ll x;scanf("%lld",&x);
for(int j=0;j<=31;j++){
ay[j]+=( (x>>j)&1 );
}
}
printf("%lld\n",find());
}
return 0;
}
J Local Minimum 水题
题意: 求出\(n×m\) 的方格里面有几个数字是:不仅是同行最小,而且是同列最小。
思路:
先找出同行最小的位置,再找出同列最小的位置,判断有几个位置两个都满足
#include <bits/stdc++.h>
using namespace std;
// #define int long long
#define pii pair<int, int>
const int N =1010;
// const int M=1000000007;
int n,m;
// vector<int> ans;
int g[N][N];
int a[N],b[N];
void slove()
{
cin>>n>>m;
int ans=0;
for(int i=0;i<n;i++){
int cnt=0x3f3f3f3f;
for(int j=0;j<m;j++){
cin>>g[i][j];
cnt=min(g[i][j],cnt);
}
a[i]=cnt;
}
for(int i=0;i<m;i++){
int cnt=0x3f3f3f3f;
for(int j=0;j<n;j++){
cnt=min(g[j][i],cnt);
}
b[i]=cnt;
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(g[i][j]<=min(a[i],b[j])){
ans++;
}
}
}
cout<<ans<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t = 1;
// cin >> t;
while (t--)
slove();
return 0;
}