CF95题解

CF95

CF95A

link

CF95A题意

LSR \texttt{LSR} LSR 特别喜欢足球。有一天他做梦,梦到了自己变成某球队的教练。

现在 LSR \texttt{LSR} LSR 要给球队起名。球队已经有一个名字,是一个只含大小写字母的字符串 w w w LSR \texttt{LSR} LSR 的队伍有 n n n 名球员,每个人都有一个名字,它们不希望自己的名字出现在球队名称中(和队名中某一段字符串一样),所以 LSR \texttt{LSR} LSR 要用别的字母替换这些子串。

这里注意六点:

  1. 对于两个字符串一样,只需读音一样(即不区分大小写),也就是说 aBC \texttt{aBC} aBC ABc \texttt{ABc} ABc 是一样的。

  2. 对于队名中的某一个字符 i i i,他替换的条件当且仅当存在 l , r ( 1 ≤ l ≤ i ≤ r ≤ ∣ w ∣ ) l,r(1 \leq l \leq i \leq r \leq \lvert w \rvert) l,r(1lirw∣),使得 [ l , r ] [l,r] [l,r] 包含在队员名字里。如果 i i i 不在任何一个与名字重复的子串里,则它不能被更改。

  3. 队员们特别喜欢一个字母,因为队员们喜欢,所以你要使这个字母出现次数最多。如果有多种解,输出字典序最小的。

  4. 替换时要注意保留原字符串格式,即大小写。

  5. 更改时要注意每个字符都要用其他字符去更改,所以如果某个需要修改的字符和队员们喜欢的字符一样,则它需要修改成别的字符。

  6. 对于每次修改后形成的新串,如果因为修改出现了新的与队员名字一样的子串,则这个子串是不需要修改的。

CF95A题解

模拟慢慢写吧

CF95A代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 110;
int n;char S[maxn];
char a[maxn][maxn];
char opt;
bool book[maxn], chg[maxn];
char tolow(char ch){if(ch >= 'A' && ch <= 'Z')return ch - 'A' + 'a';return ch;}
char toup(char ch){if(ch >= 'a' && ch <= 'z')return ch - 'a' + 'A';return ch;}
signed main(){
    scanf("%d",&n);
    for(int i = 1;i <= n;i++){
        scanf("%s",a[i] + 1);
        for(int j = strlen(a[i] + 1);j;j--)a[i][j] = tolow(a[i][j]);
    }
    scanf("%s",S + 1);scanf(" %c",&opt);int len = strlen(S + 1);
    for(int i = strlen(S + 1);i;i--){if(S[i] >= 'A' && S[i] <= 'Z'){book[i] = 1;S[i] = tolow(S[i]);}}
    for(int i = 1;i <= len;i++){
        for(int j = 1;j <= n;j++){
            bool flag = 1;
            for(int k = 1,l = strlen(a[j] + 1);k <= l;k++){
                if(S[k + i - 1] != a[j][k]){flag = false;break;}
            }
            if(flag){for(int k = 1,l = strlen(a[j] + 1);k <= l;k++)chg[k + i - 1] = 1;}
        }
    }
    opt = tolow(opt);
    for(int i = 1;i <= len;i++){
        if(chg[i]){
            if(S[i] == opt){
                if(S[i] == 'a')S[i] = 'b';
                else S[i] = 'a';
            }
            else S[i] = opt;
        }
    }
    for(int i = 1;i <= len;i++){
        if(book[i])putchar(toup(S[i]));
        else putchar(S[i]);
    }
    puts("");
    return 0;
}

CF95C

link

CF95C题意

先给你一个有边权无向图( 1 ≤ n ≤ 1 0 3 1\le n\le10^3 1n103),然后每个节点有一对值 t , c t,c t,c,表示你可以以 c c c 花费走到任意一个距离这个点小于 t t t 的点,问从 S → T S\to T ST 的最小花费

CF95C题解

读题读半天
发现点很少,直接每个节点跑一边kruskal然后暴力连边,在新图上跑一边kruskal就好了

CF95C代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
    int x = 0, f =1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * f;
}
const int maxn = 1100;

int n, m, S, T;

struct edge{
    int to, nexte, wei;
    edge(int to = 0,int ne = 0,int we = 0):to(to),nexte(ne),wei(we){}
}e[maxn * maxn * 2][2];
int tot[2],head[maxn][2];
void add(int u,int v,int w,int opt){e[++tot[opt]][opt] = edge(v,head[u][opt],w);head[u][opt] = tot[opt];}

struct node{
    int first, second;
    node(int f = 0,int s = 0):first(f),second(s){}
    friend bool operator < (node a,node b){return a.first != b.first ? a.first < b.first : a.second < b.second;}
    friend bool operator > (node a,node b){return a.first != b.first ? a.first > b.first : a.second > b.second;}
};

typedef priority_queue<node , vector<node> , greater<node> > prique;

int dis[maxn];bool book[maxn];
void kruskal(int S,int opt){
    memset(dis,0x3f,sizeof(dis));memset(book,0,sizeof(book));
    dis[S] = 0;prique que;que.push(node(dis[S],S));
    while(!que.empty()){
        int u = que.top().second;que.pop();
        if(book[u])continue;book[u] = 1;
        for(int i = head[u][opt];i;i = e[i][opt].nexte){
            int v = e[i][opt].to, w = e[i][opt].wei;
            if(dis[v] > dis[u] + w){
                dis[v] = dis[u] + w;
                que.push(node(dis[v],v));
            }
        }
    }
}

signed main(){
    n = read(); m = read(); S = read(); T = read();
    int u, v, w;
    for(int i = 1;i <= m;i++){
        u = read(); v = read(); w = read();
        add(u, v, w, 0); add(v, u, w, 0);
    }
    for(int i = 1;i <= n;i++){
        kruskal(i, 0);
        u = read(); v = read();
        for(int j = 1;j <= n;j++)
            if(dis[j] <= u){add(i,j,v,1);}
    }
    kruskal(S,1);
    printf("%lld",dis[T] == 0x3f3f3f3f3f3f3f3f ? -1 : dis[T]);
    return 0;
}

CF95E

link

CF95E题意

现在有一张 n n n 个点, m m m 条边的无向图,给出幸运数的定义是数字所有位只有 4 4 4 7 7 7
问最少连多少条边可以使得有一个连通块含有的节点数量是一个幸运数

CF95E题解

发现一只野生的水紫题
首先先按照给出的边跑并查集,算出所有连通块的大小。
然后问题就变成了:有一个数组 a 1 , 2 , . . . , t o t a_{1,2,...,tot} a1,2,...,tot,让你尽可能少的选择数字,使得其和是一个幸运数。
这玩应直接背包DP就完了~~~~~~~~吗?
O ( n 2 ) O(n^2) O(n2) 可不是闹着玩的。
注意到有一个性质,就是这个数组的和是 n n n
所以如果将相同的数字合到一起变成多重背包DP的话,最多有 n \sqrt{n} n 个选择。
然后二进制优化一下就好了
怎么有人这玩应都能写挂啊(

CF95E代码

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int x = 0, f =1;char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * f;
}
const int maxn = 1e5 + 10;
int n, m;

int fa[maxn];
int getf(int x){return x == fa[x] ? x : fa[x] = getf(fa[x]);}

int siz[maxn], buc[maxn];

int cost[maxn], val[maxn], tot;
int f[maxn], ans = 0x3f3f3f3f;

void dfs(int u){
    // printf("u = %d",u);
    if(u * 10 + 4 > n)return;
    u = u * 10 + 4;if(f[u] != 0x3f3f3f3f)ans = min(ans,f[u]);
    dfs(u);u /= 10;
    if(u * 10 + 7 > n)return;   
    u = u * 10 + 7;if(f[u] != 0x3f3f3f3f)ans = min(ans,f[u]);
    dfs(u);
}

signed main(){
    n = read(); m = read();
    for(int i = 1;i <= n;i++){fa[i] = i;}
    int u, v;
    for(int i = 1;i <= m;i++){
        u = getf(read()), v = getf(read());
        if(u != v)fa[u] = v;
    }
    for(int i = 1;i <= n;i++)siz[getf(i)]++;
    for(int i = 1;i <= n;i++)if(getf(i) == i){buc[siz[i]]++;}
    for(int i = 1;i <= n;i++){
        if(buc[i]){
            for(int j = 0;j <= 30;j++){
                if(buc[i] >= (1 << j)){
                    val[++tot] = (1 << j) * i;
                    cost[tot] = (1 << j);
                    buc[i] -= (1 << j);
                }
            }
            if(buc[i] != 0){val[++tot] = buc[i] * i;cost[tot] = buc[i];}
        }
    }
    memset(f,0x3f,sizeof(f));f[0] = 0;
    for(int i = 1;i <= tot;i++)
        for(int j = n;j >= val[i];j--)
            f[j] = min(f[j],f[j - val[i]] + cost[i]);
    // printf("tot=%d\n",tot);
    // for(int i = 1;i <= tot;i++){printf("%d %d\n",cost[i],val[i]);}
    // for(int i = 1;i <= n;i++)printf("f[%d]=%d,fa[i]=%d\n",i,f[i],fa[i]);
    dfs(0);
    printf("%d\n",ans == 0x3f3f3f3f ? -1 : (ans - 1));
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值