题目地址
出奇的差。需要好好训练了。
但是最近课程任务比较重,需要好好抓紧。PS:暂时刷刷小cf安慰下自己,明天刷套div3准备后天div3。
只有A~D的题解:E题可能会补,交互题所以随缘补。如果明天有空。明天如果不补的话可能就会鸽了。
A. Serval and Bus
好久没有做CF,A题使我自闭。
告诉多次班车的初始时间、每隔多少分钟来一次。
告诉小A到达时间,距离小A最近的是第几班车。
题解:等差数列求解刚好比小A大的班车到达时间,需要注意初始小A到达时间可能比你首班车要短。
#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
const int maxv=100000;
const int maxm=100000;
int n,t;
int ans,val=0x3f3f3f3f;
int main(){
cin>>n>>t;
FOR(i,1,n){
int x,y;
scanf("%d%d",&x,&y);
int n;
if(t>x)n=(t-x+y-1)/y+1;
else n=1;
if(x+(n-1)*y-t<val){
val=x+(n-1)*y-t;
ans=i;
}
}
cout<<ans<<endl;
}
B - Serval and Toy Bricks
告诉你三视图,给出可能情况(任意)。
题解:一开始不知道任意,懵逼了好久。
俯视图给出只有哪个点有。
左视图告诉你每行最大,前视图告诉你每列最大。
所以用俯视图判断填哪个,对于具体哪个,判断该行最大和该列最大哪个比较小,用小的填肯定不冲突。
当然不可能出现都较小,导致矛盾,如果每列最大都是3,第一行最大是4,肯定这个图画不出来。所以如果最后判断有矛盾,
局部矛盾解决了,整体还有矛盾,就是画不了图。当然题目保证一定可以..所以不需要判断。
显然复杂度O(n2)。就算非要加判断也是O(n2)
#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
int M[105][105][105];
int n,m,h;
int mb[105],ml[105];
int flag[105][105];
int Map[104][105];
int main(){
cin>>n>>m>>h;
FOR(i,1,m)scanf("%d",&mb[i]);
FOR(i,1,n)scanf("%d",&ml[i]);
FOR(i,1,n)FOR(j,1,m)scanf("%d",&flag[i][j]);
FOR(i,1,n)FOR(j,1,m)if(flag[i][j]){
if(mb[j]<=ml[i])Map[i][j]=mb[j];
else Map[i][j]=ml[i];
}
FOR(i,1,n){
FOR(j,1,m){
printf("%d ",Map[i][j]);
}
printf("\n");
}
}
C - Serval and Parenthesis Sequence
在给出的括号序列中?替换成括号,使得最后的序列前缀非法,整体合法。
题解:不合法只有可能对于前缀,左右括号不等,要保证整体合法,就要保证前缀中不能是右括号多非法,因为这种情况会导致整体非法,但左括号多非法不会导致整体非法。
把一半的左括号先填完。
当然这样肯定是最优的:如果不合法,就是已有右括号过多,左括号放满也不合法,这样会导致影响到整体的右括号多非法。
所以填完还要继续判断。
#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
int n;
string s;
int st[500005];
int R;
bool check(string s){
FOR(i,1,n){
if(s[i-1]=='(')st[++R]=1;
else R--;
//cout<<st.size()<<endl;
if(R<0)return false;
if(i!=n&&R==0)return false;
if(i==n&&R!=0)return false;
}
return true;
}
int main(){
cin>>n;
cin>>s;
int l=0,r=0;
FOR(i,1,n)if(s[i-1]=='(')l++;
if(n%2){
puts(":(");return 0;
}
FOR(i,1,n){
if(s[i-1]=='?'){
if(l<n/2){
l++;s[i-1]='(';
}else{
r++;s[i-1]=')';
}
}
}
if(!check(s))puts(":(");
else cout<<s<<endl;
}
D - Serval and Rooted Tree
每个节点的number等于:max:孩子最大,min:孩子最小
叶子结点的max、min无所谓,通过分配1~k给叶子结点,使根节点1最大。
题解在注释中。
#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
const int maxn = 300050;
int n,k;
int flag[maxn];
int dp[maxn];
vector<int>g[maxn];
void dfs(int u,int f){
if(g[u].size()==0){
dp[u]=1;k++;
/******
考虑对于这颗树、实现树的最优、自然能实现子树的最优:
子树根节点最大,自然树的根节点会最大。
把答案转换为表示第几大的数,因为数只可能出现在叶子结点中。
那么对于根节点和孩子结点,孩子结点实现最优的情况下,
根节点min: 将最优排名相加,就是根节点最优排名,不可能再好了。
max:取最小最优排名,只要能够实现这个最优,其他最优能否实现都无所谓。
dp[x]表示点x能取到的第dp[x]大的叶子结点:最后迭代出1取到的dp[1]大的叶子
为了实现最优,对于每个子树,我们都考虑前几大的叶子都在这个子树里,最后把效果叠加。
比如1->2、1->3、3->4、3->5、4->6、4->7
2:max,3:min
对于2假设都是前几大的数,(这里其实只要最大在就可以了) ,则dp[2]=min(dp[child])=1
对于3假设都是前几大的数,dp[3]+=dp[child](使min最大),dp[3]=2;
到1结点,这时候2、3需要将效果叠加:
如果1:max,取出最高排名,自然是1,因为只要在2结点实现最优情况即可(取孩子结点最大值,即最优实现)
如果1:min,首先孩子结点已经是最优结果,排名叠加即可,dp[1]=dp[2]+dp[3]=3;第3大:2为答案。
叠加也就是说:对于1个孩子结点实现最优的情况下,在这基础上对另一个孩子实现最优,dp[2]=1,第一名肯定在这里,那么
从第一名后的第二、第三都在我这里。
min把自己的孩子结点尽量设的名次够高,让所有结点都得实现最优。max只要一个孩子结点够高所以只取min(dp[child])让最小的结点实现最优其他无所谓。
*******/
return;
}
if(flag[u]){
dp[u]=0x3f3f3f3f;
for(auto v:g[u])if(v!=f){
dfs(v,u);
dp[u]=min(dp[u],dp[v]);
}
}
else{
dp[u]=0;
for(auto v:g[u])if(v!=u){
dfs(v,u);
dp[u]+=dp[v];
}
}
}
int main(){
cin>>n;
FOR(i,1,n)scanf("%d",&flag[i]);
FOR(i,2,n){
int x;
scanf("%d",&x);
g[x].push_back(i);
}
dfs(1,-1);
cout<<k-dp[1]+1<<endl;
}