【浮*光】【Codeforces Round #484 (Div. 2)】解题报告

15 篇文章 0 订阅

A.Row

#include <bits/stdc++.h>
using namespace std;

/*【Row】
你有一排椅子。 如果以下两个条件成立,我们称之为“最大”的人员座位:
1.两边都没有人。2.不可能再多一个人。
座位由一个由0和1组成的字符串给出(0表示相应的座位是空的,1-占用的)。 
目标是确定这次排的座位是否“最大”。第一个和最后一个座位不相邻(如果n≠2)。
n是椅子的数量。一串n个字符,每个字符都是0或1,描述座位。
如果座位是“最大”,输出“是”, 否则,打印“否”。 */

//【分析】在头尾各加一个0,出现三个连续0或者两个连续1则输出“No”。*/

int n; string ss;
int main(){
    scanf("%d",&n); cin>>ss;
    ss='0'+ss+'0'; //stl字符串的方便性qaq
    int now0=0; bool okk=true;
    for(int i=0;i<=n+1;i++){ //原串从0开始,且注意添加了字符
        if(ss[i]=='1'){
            if(i>0&&ss[i]==ss[i-1]){ okk=false; break; }
            now0=0;
        }
        else{
            now0++;
            if(now0==3){ okk=false; break; }
        }
    }
    if(!okk) cout<<"No"<<endl;
    else cout<<"Yes"<<endl;
    return 0;
}

B. Bus of Characters

#include <bits/stdc++.h>
using namespace std;

/*【B. Bus of Characters】
有n排座位,每排都有2个座位。第i行中两个座位的宽度均为wi厘米。
所有的整数都是不同的。公共汽车最初是空的。每个2n站有一位乘客进入巴士。
有两种类型的乘客:
1.内向者总是选择两个座位都空的,并选择宽度最小的一排其中一个座位;
2.外向者总是选择(内向)占据一个座位的位置,选择了座位宽度最大的。
请确定每位乘客将乘坐哪一排。'0'->内向的。'1'->外向的。
保证外向者等于内向者的数量(两个数字等于n)。 */

// 分析:先给wi排序。让内向者入优先队列,外向者与顶部元素一起pop。
// 提示:使用pair数组的first & second,方便储存

typedef long long ll;
const int maxn = 200005;  
pair<ll,int> a[maxn*2];  
vector<pair<ll,int> >e; //用于记录w状态
int vis[maxn];

int main(){
    int n; scanf("%d",&n);
    memset(vis,0,sizeof(vis));  
    for(int i=1;i<=n;i++){
        int x; scanf("%d",&x); 
        pair<ll,int> p;  
        p.first=x; p.second=i; //存入编号
        e.push_back(p); 
    }  
    sort(e.begin(),e.end()); //pair类型排序,x优先
    string s; cin>>s;
    priority_queue<pair<ll,int> > q; //pair类型优先队列
   
    int cnt=0; //便于计数
    for(int i=0;i<n*2;i++){ //开始上车
        if(s[i]=='0'){  //内向
            q.push(e[cnt]); //“排”的结构体存入q中,方便对应列寻找
            cout<<e[cnt].second<<" "; //输出排数的编号 
            cnt++; //“排”个数增加
        }  
        else{  //外向
            cout<<q.top().second<<" ";  q.pop(); //出队  
        }  
    }  
    return 0;  
}

C. Cut 'em all!

#include <bits/stdc++.h>
using namespace std;

/*【C. Cut 'em all!】
给一个树,问最多能删除几条边,使整个图所有相联通的部分都拥有偶数个节点。*/

/*【分析】
如果节点数是奇数,直接输出-1。首先以节点1为根节点,统计出{所有节点包含的子节点数量}。
如果该节点的儿子是奇数个,那么ans++;到了最后要记得ans-1,因为和根节点连着的边是不能去掉的。 */

const int inf = 0x3f3f3f3f;  
const int maxn = 200005;  
vector<int> e[maxn];  
int n,k,num[maxn];  
bool vis[maxn];  
      
int dfs(int x){  
    vis[x]=1;  
    for(int i=0;i<e[x].size();i++){  
        if(!vis[e[x][i]]){  
            num[x]++; num[x]+=dfs(e[x][i]);  
        }//递归求出节点个数
    }     
    return num[x];  
}  
      
int main(){  
    scanf("%d",&n);  
    for(int i=0;i<n-1;i++){ //建边
        int x,y; scanf("%d%d",&x,&y);  
        e[x].push_back(y); e[y].push_back(x);  
    }  
    if(n%2!=0){ cout<<-1<<endl; return 0; } //n为奇数
    num[1]=dfs(1); int ans=0;  
    for(int i=1;i<=n;i++)
        if(num[i]%2) ans++;  //此边要删去
    cout<<ans-1<<endl;  
    return 0;  
}  

D. Shark

#include <bits/stdc++.h>
using namespace std;

/*【D. Shark】
鲨鱼每天游ai公里,如果它一天游的距离大于等于k,我们就认为它游到了一个新的地方;
否则认为它这一天停留在原来的地方。这只鲨鱼到过的地方不会重复。
现在给出它n天游的距离(每天都不相等),我们要求出一个k,满足:
1.鲨鱼停留在每个地方的天数相等。(一天游的距离大于等于k时不算停留)。
2.停留过的地方尽可能多。 3.有多个解时k取最小值。 */

/* 分析》确定一个k,使得[小于k的子序列长度都是相等的],而且要尽可能多,最后k取最小值。
根据第三个限制,我们可以想到[最后的解一定是其中一个a的值加一]。
基于每个a都不相同这个条件我们可以对a先递增排序,顺序处理的时候因为是从小到大,
所以每次只需要增加一个满足条件的点即可。把这个点的标记置为1,分别判断它的左右是否标记过,
如果标记过就连成一个区间。至于怎么判断每个区间是否相等,我们可以维护一个最大区间的长度,
和所有区间的长度,我们还知道区间数量,所以有 最大长度*区间数量=总长度。
通过这个公式就可以判断是否区间长度都相等。  */

typedef long long ll;  
const int inf = 0x3f3f3f3f;  
const int mod = 1000000009;  
const int maxn = 100005;  
map<int, int> mp;  
int a[maxn], l[maxn], r[maxn];  
bool vis[maxn];  

int main(){  
    int n; scanf("%d",&n);  
    for (int i=1;i<=n;++i){  
        scanf("%d",&a[i]);  
        mp[a[i]]=i;//map记录每个值原来的位置  
    }  
    sort(a+1,a+n+1);  
    memset(vis,0,sizeof(vis));  
    int len=0,ans=0,p=a[n-1]+1,cnt=0;  
    for(int i=1;i<=n;++i) {  
        int pos=mp[a[i]]; vis[pos]=1;  
        if(vis[pos-1]==1&&vis[pos+1]==1){ //左右都标记过,就连成一片  
            --cnt;  
            r[l[pos-1]]=r[pos+1];//更新区间范围  
            l[r[pos+1]]=l[pos-1];//更新区间范围  
            r[pos]=r[pos+1]; l[pos]=l[pos-1];  
        } 
        else if(vis[pos-1]==1){ //左边标记过  
            l[pos]=l[pos-1]; r[pos]=pos; 
            r[l[pos-1]]=pos;  
        } 
        else if(vis[pos+1]==1){ //右边标记过  
            r[pos]=r[pos+1]; l[pos]=pos;  
            l[r[pos+1]]=pos;  
        } 
        else{ //产生一个新的区间  
            ++cnt; l[pos]=pos; r[pos]=pos;  
        }  
        int ll=r[pos]-l[pos]+1;  
        if(ll>len) len=ll;  
        if(len*cnt==i) //是否每个区间长度都相等  
            if(cnt>ans){  
                ans=cnt;  p=a[i]+1;  
            } 
    }  
    printf("%d\n", p);  
    return 0;
}  //来自https://blog.csdn.net/hcx11333/article/details/80387199 感谢dalao

E. Billiard


F. The Meeting Place Cannot Be Changed





                                                                                ——时间划过风的轨迹,那个少年,还在等你。
题目描述 给定一棵 $n$ 个节点的树,每个节点都有一个权值 $w_i$。你需要删去树上的一些边,使得剩下的每个连通块中,所有节点的权值之和不超过 $k$。 求最多能删去多少条边。 输入格式 第一行包含两个整数 $n,k$。 第二行包含 $n$ 个整数 $w_1,w_2,\cdots,w_n$。 接下来 $n-1$ 行,每行包含两个整数 $a,b$,表示节点 $a$ 和节点 $b$ 之间有一条边。 输出格式 输出一个整数,表示最多能删去的边数。 数据范围 $1\le n \le 10^5$ $1 \le k,w_i \le 10^9$ 输入样例1: 5 6 2 3 1 5 4 1 2 1 3 2 4 2 5 输出样例1: 2 输入样例2: 5 3 2 3 1 5 4 1 2 1 3 2 4 2 5 输出样例2: 0 算法1 (dfs) $O(n)$ 首先我们可以想到暴力的做法,即对于每个连通块,暴力枚举删去哪些边,尝试得到最多的删边数。那么如何求解一个连通块内的所有节点权值之和呢?我们可以使用 DFS 遍历树,对于每个节点,求出其子树内所有节点的权值之和,那么以该节点为根的子树内的所有节点权值之和就是该节点自身的权值加上其所有子节点的权值之和。 对于每个连通块,我们枚举该连通块内任意两个节点 $x,y$。如果 $x$ 与 $y$ 之间的路径上的所有边都被删去了,那么 $x$ 和 $y$ 就会分别成为两个新的连通块,这两个新的连通块内所有节点的权值之和都不超过 $k$。因此我们只需要枚举所有的 $x,y$ 对,对于每个 $x,y$ 对尝试删去它们之间的路径上的所有边,看是否能够让 $x$ 和 $y$ 成为两个新的连通块,进而更新答案即可。 时间复杂度 参考文献 python3 代码 算法2 (暴力枚举) $O(n^2)$ blablabla 时间复杂度 参考文献 C++ 代码 class Solution { public: const int N = 1e5+10; int n,k; int h[N],e[N<<1],ne[N<<1],idx; int w[N]; int sum[N]; bool st[N]; int res; void add(int a,int b) { e[idx] = b,ne[idx] = h[a],h[a] = idx++; } void dfs(int u,int father) { sum[u] = w[u]; for(int i=h[u];~i;i=ne[i]) { int j = e[i]; if(j == father) continue; dfs(j,u); sum[u] += sum[j]; } } void dfs2(int u,int father) { for(int i=h[u];~i;i=ne[i]) { int j = e[i]; if(j == father) continue; dfs2(j,u); if(sum[j] <= k) res++; else if(sum[u] - sum[j] <= k) res++; } } void solve() { cin >> n >> k; memset(h,-1,sizeof(h)); for(int i=1;i<=n;i++) cin >> w[i]; for(int i=1;i<n;i++) { int a,b; cin >> a >> b; add(a,b); add(b,a); } dfs(1,-1); dfs2(1,-1); cout << res << endl; } }; int main() { Solution().solve(); return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值