A - Prefixes
分析:模拟一下就可以了。
#include "bits/stdc++.h"
using namespace std;
int main(){
int n;
string s;
cin>>n>>s;
int ans = 0;
for (int i = 0; i < n; ++i) {
if(i&1){
if(s[i]==s[i-1]){
ans ++;
s[i]= ((s[i]-'a')^1)+'a';
}
}
}
cout<<ans<<endl<<s<<endl;
}
B - Shooting
分析:从大到小排序然后输出就可以了。证明略。
#include "bits/stdc++.h"
using namespace std;
int a[1004];
pair<int,int>p[1004];
int main(){
int n;
cin>>n;
for (int i = 1; i <= n; ++i) {
scanf("%d",&a[i]);
p[i]=make_pair(a[i],i);
}
sort(p+1,p+1+n);
int sum = 0;
for (int i = n; i >= 1; --i) {
sum += (n-i)*p[i].first+1;
}
cout<<sum<<endl;
for (int i = n; i >= 1; --i) {
printf("%d ",p[i].second);
}
}
C - White Sheet
分析:找到最大的然后把其余的补到这么多就可以了。证明略。
#include "bits/stdc++.h"
using namespace std;
int a[200004];
int main(){
int n;
cin>>n;
int maxi = 0;
for (int i = 1; i <= n; ++i) {
scanf("%d",&a[i]);
maxi = max(maxi,a[i]);
}
long long sum = 0;
int g = 0;
for (int i = 1; i <= n; ++i) {
sum += maxi - a[i];
if(a[i]!=maxi){
if(g == 0)g = maxi-a[i];
else g = __gcd(g,maxi-a[i]);
}
}
cout<<sum/g<<' '<<g<<endl;
}
D - Swords
分析:把矩形的坐标离散化一下,对于两个黑色矩形的块坐标,全部赋值为1,然后找白色的矩形上有没有不是1的就可以了。
#include "bits/stdc++.h"
using namespace std;
int x[10],y[10];
int mp[20][20];
int main(){
vector<int>vx,vy;
for (int i = 1; i <= 6; ++i) {
cin>>x[i]>>y[i];
vx.push_back(x[i]);
vy.push_back(y[i]);
}
sort(vx.begin(),vx.end());
sort(vy.begin(),vy.end());
vx.erase(unique(vx.begin(),vx.end()),vx.end());
vy.erase(unique(vy.begin(),vy.end()),vy.end());
for (int i = 1; i <= 6; ++i) {
x[i]=lower_bound(vx.begin(),vx.end(),x[i])-vx.begin()+1;
y[i]=lower_bound(vy.begin(),vy.end(),y[i])-vy.begin()+1;
}
for (int i = min(x[3]+1,x[4]+1); i <= max(x[3],x[4]); ++i) {
for (int j = min(y[3]+1,y[4]+1); j <= max(y[3],y[4]); ++j) {
mp[i][j]=1;
}
}
for (int i = min(x[5]+1,x[6]+1); i <= max(x[5],x[6]); ++i) {
for (int j = min(y[5]+1,y[6]+1); j <= max(y[5],y[6]); ++j) {
mp[i][j]=1;
}
}
bool ok = 0;
for (int i = min(x[1]+1,x[2]+1); i <= max(x[1],x[2]); ++i) {
for (int j = min(y[1]+1,y[2]+1); j <= max(y[1],y[2]); ++j) {
if(mp[i][j]!=1)ok=1;
}
}
if(ok)puts("YES");
else puts("NO");
}
E1 - Numerical Sequence (easy version)&&E2 - Numerical Sequence (hard version)
分析:E1的话可以直接暴力预处理一个前缀和,然后在前缀和里面二分找一下位置就可以了。
E2范围扩大到1e18,显然不能打前缀和的表。
对于一个数x,如果我们能找到以x结尾且x是第一次出现的位置下标(x结尾的数的下标),那么其实这个问题就比较容易去解决了,我们把这个东西称为F(n)。即F(1)=1 F(2)=3 F(3)=6
考虑一下这个序列:
1 12 123 1234 12345 123456 123456789
10 1011 101112 10111213
显然对于F(n)来说,是一个分段的等差数列,当数的位数为d时,公差为d,那么很容易,我们就可以在log的时间内求出F(n)。
显然F(n)是单调的,那么我们可以二分一下找出F(n)<=k的n的最大值,那么此时从F(n)+1位置开始的序列,是像这样的:
123456789101112……
即从这个序列中找到第k-F(n)个数(注意k==F(n)时应该取前一个),这个也可以看成一个分段的等差数列(d=0),然后这题就做完了。
#include<bits/stdc++.h>
const double eps = 1e-4;
using namespace std;
long long getdig(long long n) {
long long res = 0;
while (n) {
res++;
n /= 10;
}
return res;
}
long long getans(long long k) {
long long a = 1, base = 1;
long long res = 0;
long long dig = getdig(k);
for (int i = 1; i < dig; ++i) {
long long num = (long long)(pow(10,i-1)+eps) * 9;
long long sum = (2 * a + (num - 1) * i) * num / 2;
res += sum;
a = a + num * i+1;
}
long long num = k - (long long)(pow(10,dig-1)+eps) + 1;
res += (2*a+(num-1)*dig)*num/2;
//res+=pre;
return res;
}
string tos(long long n){
string s,t;
while(n){
t+=n%10+'0';
n/=10;
}
for (int i = t.length()-1; i >=0 ; --i) {
s+=t[i];
}
return s;
}
void ac(long long n,long long k){
for (int i = 1; i < 18; ++i) {
long long num = 1LL*(long long)(pow(10,i-1)+eps)*9*i;
if(k>num){
k-=num;
}
else {
long long x = (k+i-1)/i;
long long y = k%i;
if(y == 0) y = i;
long long t = (long long)(pow(10,i-1)+eps) + x - 1;
string s = tos(t);
putchar(s[y-1]);
puts("");
break;
}
}
}
int main() {
int q;
getans(1);
cin >> q;
while (q--) {
long long k;
cin >> k;
long long l = 1,r = 1e9;
long long ans = -1;
while(l<=r){
int mid = l+r >>1;
long long temp = getans(mid);
if(temp<=k){
ans = mid;
l=mid+1;
}
else r = mid - 1;
}
k-=getans(ans);
if(k==0){
putchar(ans%10+'0');
puts("");
}
else {
ac(ans+1,k);
}
}
}
F - Wi-Fi
分析:显而易见的是一道动态规划。
每个点分为两种状态,不开路由器,开路由器。
用dp[i][0]表示第i个位置不开路由器,且让前i个全部连接到网络的最小花费;
用dp[i][1]表示第i个位置开路由器,且让前i个全部连接到网络的最小花费。
那么容易得到转移方程:
dp[i][0]=min(dp[i-1][0]+i,dp[i-k][1],dp[i-k+1][1]……dp[i-1][1])
dp[i][1]=min(dp[i-k+1][0]……dp[i-1][0],dp[i-k+1][1]……dp[i-1][1])+i
然后找个数据结构维护一下就可以了。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 15;
char x[maxn];
long long dp[maxn][2];
int main() {
int n, k;
deque<pair<long long,long long>>q0,q1,q2;
cin >> n >> k;
cin >> x + 1;
dp[0][0] = 0;
dp[0][1]=1e9;
q0.push_back(make_pair(0,0));
q1.push_back(make_pair(1e18,0));
q2.push_back(make_pair(1e18,0));
for(int i = 1; i <= n; i++) {
dp[i][0]=1e18;
dp[i][1]=1e18;
dp[i][0] = min(dp[i][0], dp[i - 1][0] + i);
while(q0.size() && q0.front().second<i-k-1)q0.pop_front();
while(q1.size() && q1.front().second<i-k-1)q1.pop_front();
while(q2.size() && q2.front().second<i-k)q2.pop_front();
dp[i][0]=min(dp[i][0],q2.front().first);
if(x[i] == '1') {
dp[i][1]=min(dp[i][1],q0.front().first+i);
dp[i][1]=min(dp[i][1],q1.front().first+i);
}
while(q0.size() && q0.back().first > dp[i][0])q0.pop_back();
while(q1.size() && q1.back().first > dp[i][1])q1.pop_back();
while(q2.size() && q2.back().first > dp[i][1])q2.pop_back();
q0.push_back(make_pair(dp[i][0],i));
q1.push_back(make_pair(dp[i][1],i));
q2.push_back(make_pair(dp[i][1],i));
}
cout << min(dp[n][1], dp[n][0]) << endl;
return 0;
}