游记:第37届校赛

T7 悦跑圈!启动!

改完这个题我就睡觉

求在s1或s2闭合的最小环,所以我枚举了边,假设一条边的两头分别是u和v,我以为环就是dij跑出来的d[u]+d[v],虽然不会立刻原路返回起点,但是中间可能有重复路径,而环是不能有这种路径的,所以从一开始就错了。(不长记性,失恋三部曲忘了)

因为要找从s出发要回到s的最小环,路径一定是s->和s相连的某个点->和s相连的另一个点->s,如果记录和s相连的另一个点到s的路径为L1(单独的一条边),再找到在不经过L1的情况下(也就是不从入点出发,也就是从出点出发得到的最短路)从s出发到达【和s相连的另一个点】的最短路径,答案就是求和(的最小值)。

把【和s相连的某个点】记为出点集合,把【和s相连的另一个点】记为入点集合,我们只需要先分组再通过一些处理使得从s到其他所有点的最短路径一定是s->出点中的一个->……,最小的【d[入点之一]+这个入点和s之间的边长】即为所求。

分组方式是“二进制分组”,这并不是状态压缩里的那种枚举,因为我们不需要所有出点入点分组的情况,我们只需要保证任意两点都有不同组的机会。

至于“通过处理使得所有最短路从s出发后第一个经过的一定是一个出点”,可以对dij进行一下魔改,也就是提前把出点和它们的边权放进优先队列,我们拓展的起点不再是d[s]=0,而是最近的一个出点,跳过s,那些入点根本不会在第一轮进队列,也就不会被更新。

评测系统没开,代码正确性未知,先放在这里好了。

code

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e6 + 2;

int n, m, T, s1, s2;
ll d[maxn], ans = 1e9 + 15;
bool vis[maxn];

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;
}

struct node 
{
    int nxt, to, w;
}a[maxn<<1];
int head[maxn], len;

void add(int x, int y, int w)
{
    a[++len].to = y; a[len].nxt = head[x]; a[len].w = w;
    head[x] = len;
}
int vec[maxn], cnt, ps, pt, s[maxn], t[maxn];
ll vd[maxn], sd[maxn], td[maxn];

void dij(int op)
{
    memset(vis, 0, sizeof(vis));
    memset(d, 0x3f, sizeof(d));
    priority_queue<pair<int, int>, vector<pair<int,int> >, greater<pair<int, int> > > q;
    for(int i=1; i<=ps; i++)
    {
        q.push(make_pair(sd[i], s[i])); d[s[i]] = sd[i];
    }
    vis[op] = 1;
    while(!q.empty())
    {
        int u = q.top().second; q.pop();
        if(vis[u]) continue;
        vis[u] = 1;
        for(int i=head[u]; i; i=a[i].nxt)
        {
            int v = a[i].to, ds = a[i].w;
            if(vis[v]) continue;
            if(d[v] > d[u] + ds)
            {
                d[v] = d[u] + ds;
                q.push(make_pair(d[v], v));
            }
        }
    }
    for(int i=1; i<=pt; i++)
    {
        ans = min(ans, d[t[i]]+td[i]);
    }
}

int main()
{
    n = read(); m = read(); T = read();
    s1 = read(); s2 = read();
    for(int i=1; i<=m; i++)
    {
        int u = read(), v = read(), w = read();
        add(u, v, w); add(v, u, w);
    }
    //求以s1为起点的最小环
    for(int i=head[s1]; i; i=a[i].nxt)
    {
        vec[++cnt] = a[i].to; vd[cnt] = a[i].w;
    }
    for(int i=1,j=1; j<=32; i<<=1,j++)
    {
        ps = pt = 0;
        for(int k=1; k<=cnt; k++)
        {
            if(k & i) s[++ps] = vec[k], sd[ps] = vd[k];
            else t[++pt] = vec[k], td[pt] = vd[k];
        }
        if(!ps || !pt) continue;//全都进同一组了的情况有吗?
        dij(s1);
    }
    cnt = 0;//怎么第一版连这个都忘了啊
    //求以s2为起点的最小环
    for(int i=head[s2]; i; i=a[i].nxt)
    {
        vec[++cnt] = a[i].to; vd[cnt] = a[i].w;
    }
    for(int i=1,j=1; j<=32; i<<=1,j++)
    {
        ps = pt = 0;
        for(int k=1; k<=cnt; k++)
        {
            if(k & i) s[++ps] = vec[k], sd[ps] = vd[k];
            else t[++pt] = vec[k], td[pt] = vd[k];
        }
        if(!ps || !pt) continue;
        dij(s2);
    }
    ans = T / ans;
    printf("%lld", ans);

    return 0;
}

pta貌似卡快读,改scanf可以过,否则只能对一部分。

这个题还有其他写法,稍等……

T9 温柔甜妹妹——辍学版

这个逆向思维貌似很基础,但是我依然想不到,最后还是爬过去询问了出题人(%%%温柔甜妹妹)

做法写在了注释里:

code
 //但是原理是什么,应该不是找规律找到的吧
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 2e6 + 2;

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;
}

int n, t;
ll s[maxn], sum;

int main()
{
    scanf("%d%d", &n, &t);
    for(int i=1; i<=n; i++)
    {
        scanf("%lld", &s[i]);
        s[i] -= t;//相当于自己逆时针转,但是中心也转了(逆时针),因为敲击的时候自己和中心都不动
        if(s[i] < 0) s[i] += n;
        sum += s[i];//中心偏离的次数,那么我还需要逆时针转这些次才能追上逃跑的中心
    }
    for(int i=1; i<=n; i++)
    {
        printf("%lld ", (s[i]+sum)%n);//但是我肯定不用绕圈来追
    }

    return 0;
}

T10 贪吃的芙莉莲

就是下一篇博客求助的题啦,做出来全靠救济&鹤题解啦,稍微放一下防失忆叭……

%%%

code
 #include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e5 + 2;

int n, a[maxn], b[maxn];
ll ans, f[32], k;

int main()
{
    scanf("%d", &n);
    for(int i=1; i<=n; i++) scanf("%d", &a[i]);
    for(int i=1; i<=n; i++) scanf("%d", &b[i]);
    for(int i=1; i<=n; i++)
    {
        k = b[i];
        for(int j=0; j<=30; j++)
            if((1<<j)&a[i]) k = max(f[j]+b[i], k);
        for(int j=0; j<=30; j++)
            if((1<<j)&a[i]) f[j] = max(f[j], k);
        ans = max(ans, k);
    }
    printf("%lld", ans);

    return 0;
}

 

posted @   Catherine_leah  阅读(40)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2022-11-08 2022NOIPA层联测23 今天的所有题都好评!!
2022-11-08 2022NOIPA层联测22
/* */
点击右上角即可分享
微信分享提示