CF1000赛后总结

news/2024/11/30 18:35:36/

算是第一次打\(CF\)吧(而且还是\(Virtual\,participation\)),觉得题目思维含量挺高,只是两小时做\(7\)道题未免有些太困难了吧。其实主要是自己比较菜,思维水平不够, 有些知识与算法掌握得还不够多、理解不够深入。总而言之,仍要多加训练。话不多说,接下来谈题目。

Problem A. Codehorses T-shirts

题目大意:给你两张表,每张表上有\(n\)件衣服的尺码,若将尺码中的一个字母改成另一个字母(改完后仍要合法)需要花费\(1s\),那么请求出将第一张表上的尺码全部改成第二张表上的尺码最短要多长时间(不得增加或删减字母,题目保证有解)。合法的尺码有:\(M\)\(S\)\(L\)\(XS\)\(XL\)\(XXS\)\(XXL\)\(XXXS\)\(XXXL\)

思路:可以发现,只有两个长度相同的尺码可以相互更改,并且两个长度相同的尺码最多只需要花费\(1s\)就可以从一种改成另一种尺码。所以?我们只要统计出两张表中相同的尺码数\(w\),那么显然\(n-w\)就是答案,因为相同的尺码不必再改,不相同的也只需要花费\(1s\)。所以有如下代码:

#include<cstdio>
#include<iostream>
#include<map>
using namespace std;
const int N=1e2+10;
map<string,int>p;
int n,num,check[N],ans;
string s;
int main(){scanf("%d",&n);for(register int i=1;i<=n;i++){cin>>s;if(!p[s]){p[s]=++num;}check[p[s]]++;}for(register int i=1;i<=n;i++){cin>>s;if(check[p[s]])check[p[s]]--;else ans++;}printf("%d\n",ans);return 0;
}

一开始把check[p[s]]打成check[num]挂了好长时间,下次注意

Problem B. Light It Up

题目大意:一个长度为\(m\)的序列,上面有\(n\)个节点,起初灯处于开着的状态,此后从\(0\)开始每经过序列上的一个节点就会变为相反的状态,你可以在序列里安装至多\(1\)个节点,使得灯亮的时间最长。计算方法是:若灯在两个节点\(x\)\(y\)之间是亮着的,那么它发亮的时间就是\(|y-x|\)

思路:首先,若不安装多余的节点,那么\(ans\)的值应当等于\(\sum_{i=0}^{n/2}a[2*i+1]-a[2*i]\),其中\(a[i]\)表示第\(i\)个节点与\(0\)点的距离。接下来考虑若是安装了一个节点该怎么办。直接枚举\(i=[1...m-1]\)安装节点?对于\(m\le10^9\)的数据显然不现实,观察后发现\(n\)的数据范围较小,可以考虑在每个节点\(k\)左右考虑枚举\(i\),那么这样到底可不可以呢?思索一番发现,若\(k\)左侧原本是开着的,那么我们枚举\(i=(a[k-1]+1...a[k]-1)\)时显然不会有选择\(i=a[k]-1\)时优,因为前一种方法中明明可以将\(i\)向右移动使得其发亮的时间更长,而后一种方法则将这一段区间内不发亮的时间压到了最短,所以最优。如此,右侧原本是开着的同理。这样,我们先预处理若起初是关着的,到每一个节点的发亮时间,然后对于每一个节点,判断放左还是右(算出的结果其实是一样的),然后把之前预处理好的数组嫁接进去就可以了,复杂度\(O(n)\)。具体看代码:(部分冗余)

#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e5+10;
int n,m,a[N],ans,ans1[N],ans2[N];
int main(){scanf("%d%d",&n,&m);for(register int i=2;i<=n+1;i++)scanf("%d",&a[i]);a[1]=0;a[n+2]=m;n+=2;for(register int i=3;i<=n;i+=2)ans1[i]=ans1[i-2]+a[i]-a[i-1],ans1[i+1]=ans1[i];for(register int i=2;i<=n;i+=2)ans2[i]=ans2[i-2]+a[i]-a[i-1],ans2[i+1]=ans2[i];ans=ans2[n];for(register int i=1;i<=n;i++){if(a[i-1]+1!=a[i]&&i!=1){if(ans2[i]!=ans2[i-1])ans=max(ans,ans2[i]-1+ans1[n]-ans1[i-1]);else ans=max(ans,ans2[i]+1+ans1[n]-ans1[i]);}if(a[i+1]-1!=a[i]&&i!=n){if(ans2[i]!=ans2[i-1])ans=max(ans,ans2[i]-1+ans1[n]-ans1[i-1]);else ans=max(ans,ans2[i]+1+ans1[n]-ans1[i]);}}printf("%d\n",ans);return 0;
}

Problem C. Covered Points Count

题目大意:一条线段上有\(n\)个区间,每个区间的覆盖范围为\(l-r\),要求输出被覆盖了\(k=[1...n]\)次的点的个数

思路:一般的差分就是开个数组,在位置\(l\)上加一,在位置\(r+1\)上减一,然后处理完\(n\)个区间后从头扫到尾统计一下就可以了。但是,这题的\(l\)\(r\)的范围竟然已经达到了\(10^{18}\)!所以一般的方法是不行的。一开始考虑离散化,但由于压缩后内部点的个数处理不当导致程序挂掉了,于是放弃。回顾一般性方法,我们在从头扫到尾的过程中,真正起到作用的只是不为\(0\)的位置,而我们程序大部分时间都用在值为\(0\)的位置上了。类似于第二题,我们不必从头扫到尾,而是把每段区间拆成两二元组\((l,1)\)\((r+1,-1)\),并在最后按照位置顺序对所有二元组进行排序,再将这些二元组从头扫到尾即可,复杂度\(O(nlogn)\)。具体细节还是得看代码,主要是只有一个位置上所有的二元组都处理完后才可以累加答案。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=2e5+20;
int n,sum;
ll c,l,r,ans[N*10];
struct question{ll x,w;
}q[N*2];
bool cmp(question w,question e){return w.x<e.x;}
int main(){scanf("%d",&n);for(register int i=1;i<=n;i++){scanf("%lld%lld",&l,&r);q[++c].x=l;q[c].w=1;q[++c].x=r+1;q[c].w=-1;}sort(q+1,q+c+1,cmp);for(register int i=1;i<=c;i++){sum+=q[i].w;if(q[i].x!=q[i+1].x)ans[sum]+=q[i+1].x-q[i].x;}for(register int i=1;i<=n;i++)printf("%lld%c",ans[i],i==n? '\n':' ');return 0;
}

Problem D. Yet Another Problem On a Subsequence

题目大意:(此题较有趣稍后再补题解)

Problem E. We Need More Bosses

题目大意:对于一个\(n\)个点\(m\)条边的无向图,请找出两个点\(x\)\(y\),使得\(x-y\)这条路径上必须经过的边最多,并输出最多的边数

思路:必须经过的边?桥?首先我们知道若有环的话,环上任意一条边都不可能是必须经过的边,所以缩点。那么剩下的边都不属于任意一个环,也就是说,剩下的边若在原图中是\(x-y\)路径上的一条边,那么其一定是必须要经过的边,第一个条件满足。然后要求经过最多的边的个数。观察缩过点的图,无向,无环,那么它一定是一棵树。那么问题就转化成了给你一棵树,求出树上的最远点对并输出其距离,这不就是树的直径吗,简单\(dp\)一下就行了,没什么好说的,代码如下:(不喜欢大模板题)

#include<cstdio>
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
const int N=3e5+10;
int n,m,A,B,head[N],cnt,w,cl,pre[N],las[N],scc[N],f[N],dp[N],ans;
bool vis[N];
struct edge{int to,nxt;
}ed[N*2];
stack<int>S;
vector<int>G[N];
vector<int>SCC[N];
inline void addedge(int x,int y){ed[++cnt].to=y;ed[cnt].nxt=head[x];head[x]=cnt;ed[++cnt].to=x;ed[cnt].nxt=head[y];head[y]=cnt;
}
void Tarjan(int u,int fa){pre[u]=las[u]=++cl;S.push(u);for(register int i=head[u];i;i=ed[i].nxt){int v=ed[i].to;if(v==fa)continue;if(!pre[v]){Tarjan(v,u);las[u]=min(las[u],las[v]);}else if(!scc[v])las[u]=min(las[u],pre[v]);}if(pre[u]==las[u]){w++;while(1){int x=S.top();S.pop();scc[x]=w;SCC[w].push_back(x);if(x==u)break;}}
}
void DFS(int u,int fa){vis[u]=true;int k;for(register int i=0;i<G[u].size();i++){int v=G[u][i];if(v==fa||vis[v])continue;DFS(v,u);if(f[u]<f[v]+1)k=v,f[u]=f[v]+1;}dp[u]=f[u];for(register int i=0;i<G[u].size();i++){int v=G[u][i];if(v==fa||k==v)continue;dp[u]=max(dp[u],f[u]+f[v]+1);}
}
int main(){scanf("%d%d",&n,&m);for(register int i=1;i<=m;i++){scanf("%d%d",&A,&B);addedge(A,B);}Tarjan(1,0);for(register int i=1;i<=w;i++)for(register int j=0;j<SCC[i].size();j++)for(register int k=head[SCC[i][j]];k;k=ed[k].nxt)if(scc[ed[k].to]!=i){G[i].push_back(scc[ed[k].to]);G[scc[ed[k].to]].push_back(i);}DFS(1,0);for(register int i=1;i<=w;i++)ans=max(ans,dp[i]);printf("%d\n",ans);return 0;
}

Problem F. One Occurrence\((solving)\)

Problem G. Two-Paths\((solving)\)

对不起我还要去补数学和DP优化的博客,暂时搁置。。。

题解预备:POJ3696、NOI2009诗人小G及博弈论相关

转载于:https://www.cnblogs.com/ForwardFuture/p/9245597.html


http://www.ppmy.cn/news/622037.html

相关文章

CF题目_完美矩阵

CF_完美矩阵 题目链接 : Acwing. 3565完美矩阵 题目描述 如果一个矩阵能够满足所有的行和列都是回文序列&#xff0c;则称这个矩阵为一个完美矩阵。 一个整数序列 a 1 , a 2 , … , a k a_1,a_2,…,a_k a1​,a2​,…,ak​&#xff0c;如果满足对于任何整数 i&#xff08;1≤…

CF 2*1400 做题反馈

今晚做了两道题。 第一道第一部分思路是对的&#xff0c;但是第二部分出了岔子。两部分问题相似&#xff0c;我以为是跟第一部分的处理方式相似&#xff0c;就一直在凑&#xff0c;导致WA了好久。 第二道题的思路是对的&#xff0c;但是我代码不够简洁&#xff0c;导致bug…

CF寒假补题集——1.20

Contest Start There are nn people participating in some contest, they start participating in xx minutes intervals. That means the first participant starts at time 0, the second participant starts at time x, the third — at time 2⋅x, and so on. Duration of…

CF刷题(03)——难度2100~2400

这个博客记录2100到2400共17个题 2100 1.B. Maximum Value 题意&#xff1a;You are given a sequence a consisting of n n n integers. Find the maximum possible value of (integer remainder of a i a_i ai​ divided by a j a_j aj​), where 1 ≤ i , j ≤ n 1 …

CF刷题(01)——难度1600

从今天起&#xff0c;cf开刷&#xff0c;先从难度1600写起试试&#xff0c;每个难度的题目放在一个博客里面。看看最后可以写多少个博客。每一道AC的题目的代码都放在这里 &#x1f603; 1600打算写25道题后晋级1700. 最近训练重心&#xff1a;数学 math、number theory、pro…

cf-#163-总结

郁闷啊&#xff0c;本来能交第三题的。。。 自己的思维能力还是有缺陷&#xff0c;打代码的能力不行。 虽然打字速度还凑合着&#xff0c;但是没有大局观。 必须提高由思想转化成代码的速度。 做法就是多打代码~~~~

寒假cf补题

C. Monsters And Spells 怪兽会在第K秒出现&#xff0c;血量是H&#xff0c;任务是在它出现的时候消灭它。上一秒如果没有使用过魔法&#xff0c;那么这一秒只能施展1的魔法&#xff1b;如果上一秒使用过x的魔法&#xff0c;这一秒可以使用x1的魔法。若魔法x>H&#xff0c;…

CF刷题记录

dp&#xff08;3&#xff09;&#xff1a; 1、1582F1&#xff0c;a[i]的值范围很小&#xff0c;因此我们可以暴力枚举x&#xff0c;使满足条件时令dp[x^a[i]]1&#xff0c;那么怎么满足异或出的数列是递增的呢&#xff0c;我们用f[x]记录异或值为x时数列中的最大值&#xff0c;…