前言
沾了3位姐姐的光,红包抢到了不少,嘻嘻。
虽然说打铁了,但是前四发都是一发入魂,比起博主CF的A题都要WA上一发,这也太猛了。
果然,博主身边全都是猛人,只有博主是个fw。
最后缅怀一下被送走的李哥,怕不是半夜睡觉的时候被窝里全是凯南。
A - Two Subsequences (水题)
比赛链接:https://codeforces.com/problemset/problem/1602/A
题目大意
现在给出字符串 s s s,你的任务是把它拆分成两个字符串 a a a, b b b。
字符串 a a a, b b b满足:
- 字符串 a a a与字符串 b b b都是字符串 s s s的子序列;
- 字符串 s s s的所有字符 s i s_i si必须出现在字符串 a a a或字符串 b b b之中;
- 字符串 a a a的字典序要尽可能地小,字符串 b b b无所谓;
请输出符合条件的字符串 a a a, b b b。
思路
样例给了我灵感:字符串 a a a只是被限制了字典序,并没有被限制长度。
所以我们直接把字符串 s s s中字典序最小的字符单拿出来作为字符串 a a a,剩下的就是字符串 b b b。
太水了。
AC代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--){
string ss;
cin>>ss;
int pos=0;
for(int i=1;i<ss.size();i++){
if(ss[i]<ss[pos])pos=i;
}
cout<<ss[pos]<<" ";
for(int i=0;i<ss.size();i++){
if(i==pos) continue;
cout<<ss[i];
}
cout<<endl;
}
}
B - Divine Array(思维+离线处理)
比赛链接:https://codeforces.com/problemset/problem/1602/B
题目大意
现在给出一个长度 n n n( n < = 2000 n<=2000 n<=2000)的数组 a a a。
接下来我们持续对数组 a a a做同一种操作:让 a i a_i ai变成 a i a_i ai在数组中出现的次数。
例如,数组a初始为:2 1 1 4 3 1 2
。
在第一次操作之后,数组a会变为:2 3 3 1 1 3 2
在第二次操作之后,数组a会变为:2 3 3 2 2 3 2
在第三次操作之后,数组a会变为:4 3 3 4 4 3 4
在第四次操作之后,数组a会变为:4 3 3 4 4 3 4
现在有 q q q次询问,每次询问有两个数字 x x x与 k k k,请你输出 a x a_x ax在 k k k次变换之后会变成多少。
思路
了解过莫队算法的同学应该都知道离线处理的概念。
简单来说,离线处理就是将所有的询问全部先存储起来,然后一起处理掉。
对于一些题目(例如本题),题目中会给出数次询问,需要你输出每次询问的答案。
正常的做法就是问一次答一次:
第一次询问:1~4的和?
第一次回答:1~4的和为10
第二次询问:1~6的和?
第二次回答:1~6的和为21
...
但实际上,我们可以先把问题全部存储一起,然后一起回答:
询问时间:
第一次询问:1~4的和?
第二次询问:1~6的和?
...
回答时间:
第一次询问的答案:10
第二次询问的答案:21
...
能否使用离线处理的关键在于问题之间是否存在联系。
我们以本题的第一个样例举例:
假设我们是一个暴力的写法,对于每一次询问,我们都从第 0 0 0次开始暴力模拟一遍到第k次,这就浪费了大量的时间,增加了 T T T掉的风险。
而在这四次询问中我们注意到,在我们处理掉第二次询问之后,此时我们已经对数组a进行了1次操作。
这个时候我们其实是可以跳过第三次询问,先去回答第四次询问的。
所以此时我们可以使用离线处理的办法,把所有的询问全部读入之后进行排序(按照k的值由小到大)。这样的话整个寻找答案的过程中,我们其实是只是一个 O ( n ) O(n) O(n)的过程。
再观察题目中举出的例子,我们可以发现:一个数组在操作数次之后会达到一个终态(例如例子中的4 3 3 4 4 3 4
)。
所以我们在变化的时候再加以判断此次变化之后数组 a a a与变化之后的数组 a ′ a' a′是否完全一致。如果一致的话说明此时数组 a a a达到了饱和的终态,接下来再变几次都是这个数组了。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
struct node
{
int x,k,p; //第p个问题:x k
} arr[maxn];
bool cmp1(node a,node b)
{
return a.k<b.k;
}
int ans[maxn];
int a[2050];
int main()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--)
{
map<int,int> mp;
int n,q,x;
cin>>n;
for(int i=1; i<=n; i++)
cin>>a[i];
cin>>q;
for(int i=1; i<=q; i++)
{
cin>>arr[i].x>>arr[i].k;
arr[i].p=i;
}
sort(arr+1,arr+q+1,cmp1);
int cnt=0,pos=1;
int flag=0; //数组a是否达到终态
while(pos<=q)
{
if(arr[pos].k==cnt||flag)
ans[arr[pos].p]=a[arr[pos].x];
else
{
while(cnt!=arr[pos].k)
{
mp.clear();
for(int i=1; i<=n; i++)
mp[a[i]]++;
int ff=1;
for(int i=1; i<=n; i++)
{
if(a[i]!=mp[a[i]]) ff=0;
a[i]=mp[a[i]];
}
if(ff)
{
flag=1;
break;
}
cnt++;
}
ans[arr[pos].p]=a[arr[pos].x];
}
pos++;
}
for(int i=1; i<=q; i++)
cout<<ans[i]<<endl;
}
}
C. Array Elimination(位运算+GCD)
比赛链接:https://codeforces.com/problemset/problem/1602/C
题目大意
现给出长度为n的数组 a a a。
你每次可以选择数组 a a a的一个长度为 k k k的子序列 a ′ a' a′,求出这个子序列 a ′ a' a′的位与和,然后让 a ′ a' a′中的每个元素都减去这个位与和。
你可以操作无数次,最后把数组 a a a中的所有数字都变成 0 0 0。
请求出 k k k的所有值。
思路
首先先来了解一下位运算中的与运算:双1得1,其他为0。
1&1=1
1&0=0
0&1=0
0&0=0
我们把数组 a a a中的所有数都化为 2 2 2进制,并统计每一位上 1 1 1出现的个数,得到二维数组 n u m num num。
a :2 5 3
a[1]:0 1 0
a[2]:1 0 1
a[3]:0 1 1
num:1 2 2
那么我们每次的操作实际上就是让 n u m i − k num_i-k numi−k,直到把 n u m num num中所有的数都变成 0 0 0为止。
k k k的最大值也就出现了: G C D ( n u m 1 , n u m 2 , n u m 3 . . . . n u m n ) GCD(num_1,num_2,num_3....num_n) GCD(num1,num2,num3....numn)。
既然如此, k k k的所有因子显然也是满足条件的。
不过需要特判一点:如果所有的数相等,那么 k k k的值就可以是 1 1 1~ n n n中任意一个数。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
int num[40];
int main()
{
int t;
cin>>t;
while(t--)
{
int n,x;
cin>>n;
memset(num,0,sizeof(num));
for(int i=0; i<n; i++)
{
cin>>x;
int cnt=0;
while(x)
{
if(x&1)
num[cnt]++;
cnt++;
x>>=1;
}
}
vector<int> arr;
for(int i=0; i<=30; i++)
if(num[i])
arr.push_back(num[i]);
if(arr.size()==0)
{
for(int i=1;i<=n;i++)
{
if(i!=1) cout<<" ";
cout<<i;
}
cout<<endl;
}
else
{
int ans=arr[0];
for(int i=1; i<arr.size(); i++)
ans=__gcd(ans,arr[i]);
int tmp=0;
for(int i=1; i<=ans; i++)
{
if(ans%i==0)
{
if(tmp)
cout<<" ";
cout<<i;
tmp++;
}
}
cout<<endl;
}
}
}
后话
感谢阅读,希望能对你产生一点用处。
以下台词取自《银魂》第62集(红樱篇处37小天使对银时的评价):