期末考完后,重新开始刷题生涯- -
传送门:http://codeforces.com/contest/1006
种种原因,卡B题卡了很久,出了c题以后实在困得不行,DEF三题都是后来补的。
目录
-
A题:Adjacent Replacements
要将所有的奇数不变,将所有的偶数减一。。。。。。
-
B题:Polycarp's Practice
总共有n个难题,要求正好在k天,每天至少做一个难题。每个难题都有一个难度系数,当天所收益的为当天所做的题中难度系数最大的一题,要求总的收益,并列出得到最大收益的时候如何分配日期。
思路:这题写挂了挺久的。具体思路和出题者差不多,首先找到前k大的数,然后用这k个数进行延伸,求得具体的安排方案。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 20005;
int n,k;
vector<int> a,e,b;
long long ans;
int main()
{
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++)
{
int t;
scanf("%d",&t);
a.push_back(t);
b.push_back(t);
}
e.push_back(0);
sort(a.begin(),a.end(),greater<int>() );
for(int i=0;i<k;i++)
{
int t = a[i];
ans += t;
int l = find(b.begin(), b.end(), t)-b.begin()+1; //找到第一次出现的位置,这步可以用map预处理省略掉
b[l-1] = -1;
e.push_back(l);
}
cout<<ans<<endl;
sort(e.begin(),e.end());
for(int i=1;i<k;i++)
{
cout<<e[i]-e[i-1]<<" "; //两个相减得到相隔的日期
}
cout<<n-e[k-1]<<endl;//由于最后一天比较特殊,所以要用总日期减去最后一个数
return 0;
}
-
C题:Three Parts of the Array
题意:给我们n个数,要求把它们分成3段,使得第一段和第三段它们各自子序列的和相等,求满足条件的最大的子序列的和。
注意:每一段的长度可以为0!!!
题解:这题做的比第二题快多了- -, 设两个flag i=0,j=n-1,一个从前往后走,另一个从后往前走,并用l和r分别维护它们的前(后)缀和,如果某一个比较大,那么这一个就停下来让另一个走。如果两个一样大,那么就一起继续走。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 200005;
int n,d;
long long k,l,ans;
long long a[maxn];
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%lld",&a[i]);
}
int i=0,j = n-1;
k = a[i];
l = a[j];
while(i<j)
{
// cout<<i<<" "<<k<<"----"<<j<<" "<<l<<endl;
if(k==l) {
ans = k;
i++;
j--;
k+=a[i];
l+=a[j];
}
else if(k<l) {
i++;
k += a[i];
}
else if(k>l) {
j--;
l += a[j];
}
}
cout<<ans<<endl;
return 0;
}
-
D题:Two Strings Swaps
题意:给你两串长度为n的字母序列,要求仅允许完成3项操作:
- 交换a[i]和b[i];
- 交换a[i]和a[n-i+1];
- 交换b[i]和b[n-i+1];
问最少需要替换几个a中的字母,并进行若干次上述操作,才能使a和b序列一模一样。
题解:
首先用a1,a2,b1,b2来分别代表a[i],a[n-i+1],b[i],b[n-i+1]。然后判断a1,a2,b1,b2中有几个相等的,如果4个都相等,那么a中无需替换。如果3个相等,那么就要替换掉一个。其他依次类推,需要注意的是:只有a中的字母能被替换!!!
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200005;
string a,b;
int n;
long long ans;
long long cal(char a1,char a2,char b1,char b2)
{
map<char,int> mp;
mp[a1]++;
mp[a2]++;
mp[b1]++;
mp[b2]++;
vector<long long> e;
sort(e.begin(),e.end());
for(auto i:mp)
{
e.push_back(i.second);
}
if(e.size()==4) {
return 2;
}
else if(e.size()==1) {
return 0;
}
else if(e.size()==2) {
if(e[0]==2)
return 0;
else
return 1;
}
else if(e.size()==3)
{
if(a1==a2)
return 2;
return 1;
}
}
int main()
{
cin>>n;
cin>>a>>b;
int i=0,j=n-1;
for(i=0,j=n-1;i<=j;i++,j--)
{
if(i==j) {
if(a[i]!=b[j])
ans++;
}
else
ans += cal(a[i],a[n-i-1],b[i],b[n-i-1]);
//cout<<a[i]<<" "<<b[i]<<" "<<ans<<endl;
}
cout<<ans<<endl;
return 0;
}
-
E题:Military Problem
题意:给一棵树,给你若干组询问i和j,求从第i个节点开始进行深度优先遍历,输出第j个被搜索到的编号,如果没有那么多子节点,则输出-1。
题解:刚开始写了个裸的DFS,果断超时,因为面对n组询问,这样肯定过不了。
寻求改进后,可以从第一个开始遍历,记录所有节点被遍历到的顺序。但是存在有的节点,它的子节点没有那么多,但是它有兄弟节点(深度优先遍历中遍历完一个节点后会继续搜索它的兄弟节点)。所以要预处理一下每个节点的子节点个数。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
const int maxn = 200005;
long long ans,tot;
int n,q,f[maxn]; //a表示第i个的上级是谁
vector<int> e[maxn],a; //e表示第i个的下级有哪些
bool vis[maxn];
void solve(int cnt,int k)
{
a.push_back(cnt);
f[cnt] = 1;
for(int i=0;i<e[cnt].size();i++)
{
if(!vis[e[cnt][i]])
{
vis[e[cnt][i]] = 1;
solve(e[cnt][i],k);
vis[e[cnt][i]] = 0;
f[cnt] += f[e[cnt][i]];
}
}
}
int main()
{
cin>>n>>q;
for(int i=1;i<=n-1;i++)
{
int temp;
scanf("%d",&temp);
e[temp].push_back(i+1);
}
for(int i=1;i<=n-1;i++)
sort(e[i].begin(),e[i].end());
solve(1,n);
int p[maxn];
memset(p,-1,sizeof(p));
for(int i=0;i<a.size();i++)
{
//cout<<a[i]<<" ";
p[a[i]] = i;
}
//cout<<endl;
//for(int i=1;i<=n;i++)
// cout<<f[i]<<" ";
//cout<<endl;
for(int i=0;i<q;i++)
{
int u,k;
scanf("%d%d",&u,&k);
if(p[u]+k<=n&&f[u]>=k)
cout<<a[p[u]+k-1]<<endl;
else
cout<<-1<<endl;
}
return 0;
}
-
F题:Xor-Paths
题意:从(1,1)点开始,只向右或者下走,要求走到(n,m)点。每个点有一个值a[i][j],要求所走路径的所有值xor起来等于k,问这样的路径有几条。
题目思路:这是一条不错的题目。
首先想到的就是普通的dfs,但是会超时。所以学习使用双向DFS,从(0,0)点开始搜索,到x+y-1=max(m,n)为止,而这些终止的点在图上看是一条直线(y=-x+1+max(m,n) ),再用一个dfs从后往前(只能往左或往上)搜,同样是搜到x+y-1=max(m,n)为止,这样它们的终止节点也在这条线上,并且这可以看作是从头走到尾(自己理解一下)。中间有一些用map处理的技巧,需要自己了解。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 25;
long long n,m,k,a[maxn][maxn],ans=0,mn;
map<long long,long long> mp[25]; //刚开始map<int,long long>没有过,但是map<long long,int>过了
int dir[4][2] = {1,0,0,1,-1,0,0,-1};
void dfs1(long long x,long long y,long long cnt)//从前往后时,开始的xor值为0
{
cnt ^= a[x][y]; //先异或再判断,保证到达中间那条线时是异或过那个结果的
if(x+y+1==mn)
{
mp[x][cnt]++; //由于是一条斜线,所以x值不会重复,用mp[x][now]来记录走到这个节点,异或值为cnt的路线条数
return;
}
if(x+1<n) dfs1(x+1,y,cnt);
if(y+1<m) dfs1(x,y+1,cnt);
}
void dfs2(long long x,long long y,long long cnt) //从k开始异或,异或到中间那条线,满足条件的异或值为之前求得的cnt的值,路线个数就是mp[x][cnt]
{
if(x+y+1==mn)
{
ans += mp[x][cnt];
return;
}
cnt ^= a[x][y]; //先判断再异或,保证中间那条线不会被运算两次
if(x-1>=0) dfs2(x-1,y,cnt);
if(y-1>=0) dfs2(x,y-1,cnt);
}
int main()
{
cin>>n>>m>>k;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
scanf("%lld",&a[i][j]);
mn = min(n,m); //也可以是max(n,m),并不影响结果
dfs1(0,0,0);
dfs2(n-1,m-1,k);
cout<<ans<<endl;
return 0;
}