很久没有写题解了。
今年桂电比赛又开始了,本来想比初赛的结果在高铁上就没有写。拖了两天来补个题解吧。
(题解顺序是解题顺序)
H题 分离
https://ac.nowcoder.com/acm/contest/558/H
签到题,纯粹模拟,没什么难度。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll _max = 1e9;
int main(){
int n;
string s1;
cin>>n;
while(n--){
cin>>s1;
for(int i = 0 ; i < s1.length() ; i+=2)
cout<<s1[i];
cout<<endl;
}
return 0;
}
B题 重复
https://ac.nowcoder.com/acm/contest/558/B
会map容器的,应该一分钟。不会的可以用的方法也有很多,比如转成哈希表,字符串最小表达(我不知道这个会不会TLE)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll _max = 1e9;
map<string,bool> mp;
int main(){
int n,m;
cin>>n>>m;
string s;
while(n--){
cin>>s;
mp[s] = 0;
}
cout<<mp.size()<<endl;
return 0;
}
I题 选择
https://ac.nowcoder.com/acm/contest/558/I
这是一道很明显的DP题。选取ai后不能选ai+1。
所以每个位置有两个状态,假设0:不选该数,1:选取该数
那么DP方程就显而易见dp[i][0] = max(dp[i-1][1],dp[i-1][0])
dp[i][1] = max(dp[i-1][0]+ai,dp[i-1][1])
然后遍历一遍就OK了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll _max = 105;
int a[_max];
int dp[_max][2];
int main(){
int T,n;
cin>>T;
while(T--){
cin>>n;
memset(dp,sizeof(dp),0);
for(int i = 1 ; i <= n ; i++) cin>>a[i];
for(int i = 1 ; i <= n ; i++){
dp[i][0] = max(dp[i-1][1],dp[i-1][0]);
dp[i][1] = max(dp[i-1][0]+a[i],dp[i-1][1]);
}
cout<<(dp[n][0],dp[n][1])<<endl;
}
return 0;
}
J题 相聚
这题和去年蓝桥杯的海岛题目很像。BFS搜索,vis做记号就行了。很久没写题目了,很傻逼的最开始mp开的int弄得我一直在想怎么输入不了。。。傻逼了。。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll _max = 55;
char mp[_max][_max];
bool vis[_max][_max];
int n,m;
int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};
struct node{
int x,y;
void init(int x,int y){
this->x = x;
this->y = y;
}
};
bool IsOut(node n1){
if(n1.x > n || n1.x < 1) return false;
if(n1.y > m || n1.y < 1) return false;
if(vis[n1.x][n1.y]) return false;
return true;
}
void bfs(int x,int y){
queue<node> q;
while(!q.empty()) q.pop();
node now,next;
now.init(x,y);
q.push(now);
vis[now.x][now.y] = 1;
while(!q.empty()){
now = q.front();
q.pop();
for(int i = 0 ; i < 4 ; i++){
next.init(now.x+dx[i],now.y+dy[i]);
if(!IsOut(next)) continue;
vis[next.x][next.y] = 1;
q.push(next);
}
}
return ;
}
int main(){
int T,cnt;
cin>>T;
while(T--){
memset(vis,false,sizeof(vis));
cnt = 0;
cin>>n>>m;
for(int i = 1 ; i <= n ; i++)
for(int j = 1 ; j <= m ; j++){
cin>>mp[i][j];
if(mp[i][j] == '0') vis[i][j] = 1;
}
for(int i = 1 ; i <= n ; i++)
for(int j = 1 ; j <= m ; j++){
if(vis[i][j]) continue;
cnt++;
bfs(i,j);
}
cout<<cnt<<endl;
}
return 0;
}
E题 区间
这个题目有人说他用上升子序列做TLE了。这里说一下平常的DP中求得最长上升子序列和这道题是不一样的。最长上升子序列要求的不是一个连续的子序列。这里明显是了。这也是一道模拟题,自己理清一下思路,也很简单的。。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll _max = 1005;
int a[_max];
int main(){
int T,n;
cin>>T;
while(T--){
cin>>n;
for(int i = 1 ; i <= n ; i++) cin>>a[i];
int l = 1,r = 1;
int l_ans = 1,r_ans = 1,len = 0;
for(int i = 2 ; i <= n ; i++){
if(a[i] >= a[i-1]) r = i;
else{
if(len < r-l){
l_ans = l;
r_ans = r;
len = r-l;
}
l = i;
}
}
if(len < r-l){
l_ans = l;
r_ans = r;
}
cout<<l_ans<<' '<<r_ans<<endl;
}
return 0;
}
A题 串串
这是一道模板题,如果不知道后缀数组的自然很难做,知道了就是一个标准模板题,所以直接放代码就好了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 100005;
char ch[MAXN], All[MAXN];
int SA[MAXN], Rank[MAXN], Height[MAXN], tax[MAXN], tp[MAXN], a[MAXN], n, m;
char str[MAXN];
//Rank[i] 第i个后缀的排名; SA[i] 排名为i的后缀位置; Height[i] 排名为i的后缀与排名为(i-1)的后缀的LCP
//tax[i] 计数排序辅助数组; tp[i] Rank的辅助数组(计数排序中的第二关键字),与SA意义一样。
//a为原串
void RSort() {
for (int i = 0; i <= m; i ++) tax[i] = 0;
for (int i = 1; i <= n; i ++) tax[Rank[tp[i]]] ++;
for (int i = 1; i <= m; i ++) tax[i] += tax[i-1];
for (int i = n; i >= 1; i --) SA[tax[Rank[tp[i]]] --] = tp[i];
}
int cmp(int *f, int x, int y, int w) { return f[x] == f[y] && f[x + w] == f[y + w]; }
void Suffix() {
//SA
for (int i = 1; i <= n; i ++) Rank[i] = a[i], tp[i] = i;
m = 127 ,RSort();
for (int w = 1, p = 1, i; p < n; w += w, m = p) {
for (p = 0, i = n - w + 1; i <= n; i ++) tp[++ p] = i;
for (i = 1; i <= n; i ++) if (SA[i] > w) tp[++ p] = SA[i] - w;
RSort(), swap(Rank, tp), Rank[SA[1]] = p = 1;
for (i = 2; i <= n; i ++) Rank[SA[i]] = cmp(tp, SA[i], SA[i - 1], w) ? p : ++ p;
}
int j, k = 0;
for(int i = 1; i <= n; Height[Rank[i ++]] = k)
for( k = k ? k - 1 : k, j = SA[Rank[i] - 1]; a[i + k] == a[j + k]; ++ k);
}
void Init() {
scanf("%s", str);
n = strlen(str);
for (int i = 0; i < n; i ++) a[i + 1] = str[i];
}
int main() {
Init();
Suffix();
ll ans = 0;
for(int i = 1 ; i <= n ; i++) ans += n-SA[i]+1-Height[i];
printf("%lld\n",ans);
return 0;
}
C题 二元
这题其实我很蠢,首先我是想二分答案来做,然后太久没写代码了。二分想了会,尴尬,然后TLE了。然后重新思考了下数据范围觉得二分答案确实不可行,然后就觉得那肯定是模拟了。就先排序了一边,想着用a为规则从大到小排序,然后前k个全选就有了一个可选内最大的a,然后根据最后要求的a+b最大值的条件来替代已选的元素。那就肯定要队列了。然后因为替换的是最小的就是优先队列了。既然一开始排序是a规则,那优先队列就是b了。想通了就OK了。
#include<bits/stdc++.h>
#define inf 1e9+7;
using namespace std;
typedef long long ll;
const int _max = 1e5+5;
struct node{
int a,b;
bool operator < (const node &n1)const{
return b > n1.b;
}
}n[_max];
bool cmp(node n1,node n2){
return n1.a > n2.a;
}
int main(){
priority_queue<node> q;
int N,k;
cin>>N>>k;
for(int i = 1 ; i <= N ; i++) cin>>n[i].a>>n[i].b;
sort(n+1,n+N+1,cmp);
for(int i = 1 ; i <= k ; i++) q.push(n[i]);
int ans = n[k].a + q.top().b;
for(int i = k+1 ; i <= N ; i++){
q.pop();
q.push(n[i]);
if(ans < n[i].a+q.top().b){
ans = n[i].a+q.top().b;
}
}
cout<<ans<<endl;
return 0;
}
F题 点对
这道题目,首先我骂自己是个憨憨,改BUG改了二十分钟,全是莫名其妙的错误,怕不是到了饭点想吃饭了?
分析一下题目,找对点,有对点代表有环,这个很明显的。一个n个节点的环,那么他的对点有n*(n-1)/2对,这个应该挺容易推的就不推了。所以我们要做的就是首先建图,然后把非环的点排除掉。这个地方既然想到了环,自然而然就肯定要想到拓扑,然后我们首先预处理这个图,然后非环的节点排除,然后我们要做的就是把环与环之间分开来。每个环的权值是n*(n-1)/2。然后最后加起来就OK了。
#include<bits/stdc++.h>
#define inf 1e9+7;
using namespace std;
typedef long long ll;
const int _max = 305;
vector<int> mp[_max];
vector<int> mp1[_max];
int in_degree[_max],out_degree[_max];
int vis_p[_max];
bool vis_mp[_max][_max];
int dfs(int s,int cnt){
vis_p[s] = 2;
for(int i = 0 ; i < mp[s].size() ; i++){
int e = mp[s][i];
if(vis_p[e] == 1 || vis_mp[s][e]) continue;
vis_mp[s][e] = 1;
cnt += dfs(e,vis_p[e] == 0);
}
return cnt;
}
int main(){
int n,m,u,v;
cin>>n>>m;
memset(in_degree,0,sizeof(in_degree));
memset(out_degree,0,sizeof(out_degree));
memset(vis_p,0,sizeof(vis_p));
memset(vis_mp,0,sizeof(vis_mp));
for(int i = 1 ; i <= m ; i++){
cin>>u>>v;
mp[u].push_back(v);
mp1[v].push_back(u);
in_degree[v]++;
out_degree[u]++;
}
for(int i = 1 ; i <= n ; i++){
if(!vis_p[i] && (!in_degree[i] || !out_degree[i])){
vis_p[i] = 1;
if(!in_degree[i]){
out_degree[i] = 0;
for(int k = 0 ; k < mp[i].size() ; k++){
int s = mp[i][k];
in_degree[s]--;
}
}
else{
in_degree[i] = 0;
for(int k = 0 ; k < mp1[i].size() ; k++){
int s = mp[i][k];
out_degree[s]--;
}
}
i = 0;
}
}
int ans = 0;
for(int i = 1 ; i <= n ; i++){
if(vis_p[i]) continue;
int ret = dfs(i,1);
ans += ret*(ret-1)/2;
}
cout<<ans<<endl;
return 0;
}
做到现在大概已经做了将近三个半小时吧,很久没做题真是老了很多。还是要好好写题啊(呵呵,我就说说,毕设不要做了?)。还有两题就明天写了。晚上可能还要看看英语。加油吧。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
补说一下,我很绝望的查了一下F题,然后看到了一个人的解法。我突然觉得我很傻逼。只有300的数据范围。为什么不暴力呢。对不起,我是个睿智。