acwing第七场周赛

第二题
求第k个字符串
3759. 第k个字符串
题目
提交记录
讨论
题解
视频讲解

给定两个整数 n 和 k。

用 n−2 个 a 和 2 个 b 来构成一个字符串,则一共可以构成 n(n−1)2 个不同的字符串。

将这 n(n−1)2 个字符串按照字典序进行排序。

请输出排好序后,排在第 k 个的字符串。

例如,当 n=5,k=2 时,共可以生成 10 个不同的字符串,按字典序排列如下:

aaabb
aabab
aabba
abaab
ababa
abbaa
baaab
baaba
babaa
bbaaa
其中,排在第 2 个的字符串为 aabab。

输入格式
第一行包含整数 T,表示共有 T 组测试数据。

每组数据占一行,包含两个整数 n 和 k。

输出格式
每组数据输出一行结果,表示答案。

数据范围
前三个测试点满足 1≤T≤10,1≤n≤20,1≤k≤100。
所有测试点满足 1≤T≤10000,3≤n≤105,1≤k≤min(2×109,n(n−1)2)。
同一测试点内所有 n 的和不超过 105。

输入样例:
7
5 1
5 2
5 8
5 10
3 1
3 2
20 100
输出样例:
aaabb
aabab
baaba
bbaaa
abb
bab
aaaaabaaaaabaaaaaaaa


#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#define int long long
using namespace std;
signed main(){
    int t;
    int n,k;
    cin>>t;
    while(t--){
        cin>>n>>k;
        k--;
        vector<char>v(n,'a');
        int i=1;
        for(;i<n;i++){
            int p1=(i-1)*i/2;
            int p2=(i+1)*i/2;
            if(p1<=k&&k<p2)break;
        }
        int j=k-i*(i-1)/2;
        v[n-i-1]='b';
        v[n-j-1]='b';
        //reverse(v.begin(),v.end());
        for(int x=0;x<n;x++)cout<<v[x];
        cout<<endl;
    }
}

我们的重点在于如何放置两个字符 , 不妨首先考虑放置第一个字符 。对于一个给定长度为  的字符串,假设其第  个位置为字符 (,否则没有地方放第二个  ),那么第二个字符  可以放置的位置为  范围,共有  种可能;同理,若第  个位置放置第一个字符 ,则第二个字符  有  种放置的可能。所以第一个字符  放置在第  个位置对应的字符串分别是第 个字符串。所以我们首先确定第一个  的位置,再从其对应的  个字符串中找到第二个  的位置。
代码:(C++)
#include <bits/stdc++.h>
using namespace std;
using ll = long long; 

int main() {
    int T;
    cin >> T;
    while(T--) {
        ll n, k;
        cin >> n >> k;
        k--;
        vector<char> v(n, 'a');

        ll first = 0; // 第一个 b 的位置

        for(first = 1; first < n; first++) {
            ll c = (first - 1) * (first) / 2;
            ll d = (first) * (first + 1) / 2;
            if(c <= k && d > k) break;
        }
        ll second = k - (first - 1) * (first) / 2; // 第二个 b 的位置


        v[first] = 'b';
        v[second] = 'b';

        // 上面是自左向右排布的,题意要求从右向左
        reverse(v.begin(), v.end());

        for(int i = 0; i < n; i++) cout << v[i];
        cout << endl;

    }
}

一个国家由 n 个城市组成,这 n 个城市由 n−1 条双向道路连接,呈一个树形结构。

每个城市都设有加油站,在第 i 个城市可以购买 wi 升汽油。

汽车在道路上行驶,毫无疑问也会消耗汽油,每条道路的具体耗油量也会给出。

现在,需要制定一条汽车的行进路线,从任意城市 s 出发,经过一条简单路径,到达任意城市 e 结束。

注意,行进路线也可以只包含一个城市(也就是哪都没去)。

汽车初始时油箱是空的,但是可以在路线中经过的每个城市购买汽油,包括开始城市和最终城市。

如果在一条行进路线中,汽车沿一条道路从某一城市开往另一城市时,现有油量小于该条道路所需油量,那么就说明这条行进路线行不通。

请问,在保证行进路线合理的情况下,汽车在抵达最终城市后,可以剩余的最大油量是多少?

再次提醒,汽车在最终城市也可以加油。

输入格式
第一行包含整数 n。

第二行包含 n 个整数 w1,w2,…,wn。

接下来 n−1 行,每行包含三个整数 u,v,c,表示城市 u 和城市 v 之间存在一条双向道路,耗油量为 c。

输出格式
一个整数,表示可能的最大剩余油量。

数据范围
前三个测试点满足,1≤n≤5。
所有测试点满足,1≤n≤3×105,0≤wi≤109,1≤u,v≤n,1≤c≤109,u≠v。

输入样例1:
3
1 3 3
1 2 2
1 3 2
输出样例1:
3
输入样例2:
5
6 3 2 5 0
1 2 10
2 3 3
2 4 1
1 5 1
输出样例2:
7

#include <iostream>
#include <cstring>
#include <algorithm>

#define ll long long
using namespace std;

const int N = 3e5 + 10;
ll e[2 * N], h[2 * N], ne[2 * N], idx, w[2 * N];//建图
ll a[N];
ll n,ans;

void add(int a, int b, int c)  // 添加一条边a->b,边权为c
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

ll dfs(int u, int father)//每个点更新一下最大值 最后返回全局最大值
{
    ll d1 = 0, d2 = 0; //找到最大点和次大点
    for (int i = h[u]; i != -1; i = ne[i])//遍历子节点 枚举最高点
    {
        int j = e[i];//子节点
        if (j == father) continue;//如果子节点是父节点 防止往回搜
        ll d = dfs(j, u);
        if (d < w[i]) continue;//往下走这条边油量不够
        d -= w[i];
        if (d >= d1) d2 = d1, d1 = d;//更新最大值
        else if (d >= d2) d2 = d;//更新次大值
    }
    ans = max(ans, d1 + d2 + a[u]);//次大值+最大值+顶点的油量
    return d1 + a[u];
}

int main()
{
    scanf("%lld", &n);
    memset(h, -1, sizeof h);//初始化
    for (int i = 1; i <= n; i ++ ) scanf("%lld", &a[i]);
    for (int i = 1; i < n; i ++ )
    {
        ll u, v, c;
        scanf("%lld%lld%lld",&u,&v,&c);
        add(u, v, c);//双向边
        add(v, u, c);
    }
    ans = max(ans, dfs(1, -1));
    cout << ans << endl;
    return 0;
}

作者:被大佬嘲笑的小菜鸡
链接:https://www.acwing.com/solution/content/56317/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
#include <iostream>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
const int N = 300010;
int w1[N],h[N],ne[N*2],w[N*2],e[N*2];
int n,a,b,c,idx,vis[N],st[N],v[N];
int dp[N],ans;
void add(int a,int b,int c){
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

int dfs(int u,int fa){
    int d1=0,d2=0;
    for(int i=h[u];~i;i=ne[i]){
        int j=e[i];
        if(j==fa)continue;
        int d=dfs(j,u);
        d-=w[i];
        if(d>=d1)d2=d1,d1=d;
        else if(d>d2)d2=d;
        
    }
    ans=max(ans,d1+d2+w1[u]);
    return d1+w1[u];
}

signed main(){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>w1[i];
    memset(h, -1, sizeof h);
    int m=n-1;
    while(m--){
        cin>>a>>b>>c;
        add(a,b,c);
        add(b,a,c);
        
        v[a]++;
        v[b]++;
    }
    int root=1;
    for(int i=1;i<=n;i++){
        if(v[i]==2)root=i;
    }
    dfs(root,-1);
    cout<<ans;
    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值