比赛链接
A
正常枚举就行,从最后一位往前枚举,-1、-2、-3...这样
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
const int N = 1010;
int a[N];
void solve()
{
int x, y, n;
cin >> x >> y >> n;
a[1] = x, a[n] = y;
int res = 1;
for(int i = n - 1; i >= 1; i --)
{
a[i] = a[i + 1] - res;
res ++;
}
if(a[1] < x)
{
cout << -1 << endl;
return;
}
a[1] = x;
for(int i = 1; i <= n; i ++)cout << a[i] << ' ';
cout << endl;
}
int main()
{
IOS
int _;
cin >> _;
while(_ --)
{
solve();
}
return 0;
}
B
第一个操作可以做到把所有在奇数位的字母任意排列以及把所有在偶数位的字母任意排列。
第二个操作,如果k是奇数时没有任何影响,只用第一种情况就能最优;如果k是偶数时,而且k又小于n,每次操作可以做到使区间内奇偶对调,进行两次操作[i,i+k-1],[i+1,i+k],这样只会发生两个数的下标奇偶性发生改变,然后这样就可以实现任意排序。
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
void solve()
{
int n, k;
cin >> n >> k;
string A, B;
string s;
cin >> s;
for(int i = 0; i < s.size(); i ++)
{
if(i % 2)B.push_back(s[i]);
else A.push_back(s[i]);
}
sort(A.begin(), A.end());
sort(B.begin(), B.end());
sort(s.begin(), s.end());
if(k % 2)
{
n = (n + 1) / 2;
for(int i = 0; i < n; i ++)
{
if(i < A.size())cout << A[i];
if(i < B.size())cout << B[i];
}
}
else cout << s;
cout << endl;
}
int main()
{
IOS
int _;
cin >> _;
while(_ --)
{
solve();
}
return 0;
}
C
可以先把x变成2的i次方,在这个过程中用2的i次方减,一个数一定可以由若干2的i的方组成,x到2的i次方过程中减掉数的总和也一定可以由若干2的i次方组成。
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
ll a[50];
bool st[50];
void solve()
{
vector<int> ans;
int x;
cin >> x;
int l = 1, r = 32;
while(l < r)
{
int mid = l + r + 1 >> 1;
if(a[mid] <= x)l = mid;
else r = mid - 1;
}
if(a[l] == x)
{
while(x != 1)
{
ans.push_back(x);
x >>= 1;
}
ans.push_back(1);
cout << ans.size() << endl;
for(int i = 0; i < ans.size(); i ++)
{
cout << ans[i] << ' ';
}
cout << endl;
return;
}
memset(st, false, sizeof st);
while(x > a[l])
{
int tmp = x - a[l];
int L = 1, R = 32;
while(L < R)
{
int mid = L + R + 1 >> 1;
if(a[mid] <= tmp)L = mid;
else R = mid - 1;
}
for(int i = L; i >= 0; i --)
{
if(st[i])continue;
if(x % a[i] == 0)
{
ans.push_back(x);
x -= a[i];
st[i] = true;
break;
}
}
}
while(x != 1)
{
ans.push_back(x);
x >>= 1;
}
ans.push_back(1);
cout << ans.size() << endl;
for(int i = 0; i < ans.size(); i ++)
{
cout << ans[i] << ' ';
}
cout << endl;
return;
}
int main()
{
IOS
a[0] = 1;
for(int i = 1; i <= 40; i ++)a[i] = a[i - 1] * 2;
int _;
cin >> _;
while(_ --)
{
solve();
}
return 0;
}
D
这题有点dp那味儿了,总的来说应该是前缀和,太久没写题手生了,写了一个多小时www
看那个公式画个图可以看出来它可以将一个点往下沿等腰三角形延伸,三角形区域内的点都要转换。
可以发现最上面那层有多少个1这一层肯定就要转换多少次,转换完后看第二层也类似,第二层有多少个需要转换的点这一层就要转换多少次,所以一定需要遍历一遍,往后以此类推。所以其实就是从第一层到最后一层遍历一遍所有点,遇到一个需要执行操作的点答案就加一。
现在复杂度已经到达n方了,我们需要想一个O(1)或者logn的算法来计算每个点需不需要转换,然后想到了前缀和,然后加一个额外的二维数组来统计每个点上方的等腰三角形区域内有多少个点被执行过操作。
然后前缀和转化成斜着的,同样是记录有多少个点被执行过操作。一个从左上到右下的斜前缀和数组,一个从右上到左下的斜前缀和数组。
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
const int N = 3010;
int n;
char s[N][N];
int a[N][N], dg[N][N], udg[N][N];
void solve()
{
cin >> n;
for(int i = 0; i <= n + 1; i ++)
for(int j = 0; j <= n + 1; j ++)
a[i][j] = dg[i][j] = udg[i][j] = 0;
for(int i = 1; i <= n; i ++)cin >> s[i] + 1;
int ans = 0;
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j <= n; j ++)
{
a[i][j] = a[i - 1][j] + dg[i - 1][j - 1] + udg[i - 1][j + 1];
int tmp = a[i][j] + s[i][j] - '0';
if(tmp % 2)
{
ans ++;
a[i][j] ++;
dg[i][j] ++;
udg[i][j] ++;
}
dg[i][j] += dg[i - 1][j - 1];
udg[i][j] += udg[i - 1][j + 1];
}
}
cout << ans << endl;
}
int main()
{
IOS
int _;
cin >> _;
while(_ --)
{
solve();
}
return 0;
}