《信奥联赛》

sleep函数:

其实这题想考察的是欧拉函数的性质。但是后面发现,其实光看也能明白了。

对于一个质数,在[1,x)里的所有数肯定和他互质即y = x-1,那么他的sleep函数值肯定是x – (x-1) = 1。

对于一个非质数的数,他的y肯定<x-1。那么这个题就转变为求区间内的最大质数。

没有怎么卡复杂度,直接nlogn求一下就行了。貌似正着跑会被小卡一下。

因为求最大,所以从大往小扫即可会快一些。时间复杂度O(nlogn)

std:

#include<bits/stdc++.h>
using namespace std;
#define dbg(ax) cout << "now this num is " << ax << endl;

bool check(int x)
{
    if(x < 2) return false;
    int m = sqrt(x);
    for(int i = 2;i <= m;++i)
    {
        if(x%i == 0) return false;
    }
    return true;
}
int main()
{
    int a;
    scanf("%d",&a);
    if(a < 2) printf("%d
",a);
    else for(int i = a;i >= 1;--i) if(check(i)){printf("%d
",i);break;}
    return 0;
}

View Code

levil的数组:

这题,先给出一种较暴力的做法,因为这题想当个签到题,没怎么细化。

我们直接从最小或者最大开始删点,当中间无法删了,说明接下来这个点也无法删去了。

因为对于它相邻的点,只可能删去变大,那么他们的差距只会变大。显然更不符合了。

所以小顶堆模拟这个删点的过程即可。

std:

#include<bits/stdc++.h>
using namespace std;
#define dbg(ax) cout << "now this num is " << ax << endl;

typedef pair<int,int> pii;
int a[105],vis[105];
priority_queue<pii,vector<pii>,greater<pii> > Q;
int main()
{
    int n;
    scanf("%d",&n);
    for(int i = 1;i <= n;++i) 
    {
        scanf("%d",&a[i]);
        Q.push(pii{a[i],i});
    }
    while(Q.size() > 1)
    {
        int u = Q.top().second;
        int val = Q.top().first;
        Q.pop();
        vis[u] = 1;
        int le = u-1,ri = u+1;
        while(vis[le] && le > 0) le--;
        while(vis[ri] && ri <= n) ri++;
        if(le < 1 && ri > n) break; 
        if(le >= 1 && abs(val-a[le]) <= 1) continue;
        if(ri <= n && abs(val-a[ri]) <= 1) continue;
        break;
    }
    if(Q.size() > 1) printf("NO
");
    else printf("YES
");
    return 0;
}

View Code

现在考虑一下优化:对于每个位置,我们考虑是否能删去他。

那么,显然能删掉他的点,就是他左边第一个比他大的数,和右边第一个比他大的数。

所以我们只需要用单调栈来维护出每个点左边第一个大的数和右边第一个大的数即可。时间复杂度O(n)

levil的小岛:

注意这里的边权是2^i。

满足一个性质2^i > 2^(i-1) + 2^(i-2) + 2^(i-3) + .. +2^0。

所以当我们越之前能走到这个点肯定比后面的边权要小。

那么,我们对应输入的顺序去维护一棵最小生成树。

然后我们再考虑去计算这个总边权。如果我们枚举出每条路径,来计算总值,显然是过于暴力的做法。

我们考虑去计算每条边的贡献,可以发现,每条边的贡献,就是dp[u] * (n-dp[u]) ;dp[u]表示u的子树里的点数量。

所以我们只需要树形dp统计出每个点的子树里的节点数量即可。

std:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5+5;
const LL Mod = 1e9+7;
#define rg register
struct Node{int to;LL dis;};
vector<Node> G[N];
int n,m,fa[N],ssize[N];
int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);}
LL ans = 0;
void dfs(int u,int fa,LL dis)
{
    ssize[u] = 1;
    for(auto v : G[u])
    {
        if(v.to == fa) continue;
        dfs(v.to,u,v.dis);
        ssize[u] += ssize[v.to];
    }
    if(u != 1)
    {
        LL num = n-ssize[u];
        ans = (ans+num*ssize[u]%Mod*dis%Mod)%Mod;
    }
}
int main()
{
    scanf("%d %d",&n,&m);
    for(rg int i = 1;i <= n;++i) fa[i] = i;
    LL pre = 1;
    while(m--)
    {
        pre = pre*2%Mod;
        int u,v;scanf("%d %d",&u,&v);
        if(u == v) continue;
        int xx = Find(u),yy = Find(v);
        if(xx != yy)
        {
            fa[xx] = yy;
            G[u].push_back(Node{v,pre});
            G[v].push_back(Node{u,pre});
        }
    }
    dfs(1,0,0);
    printf("%lld
",ans);
    system("pause");     
    return 0;
}

View Code

然后我们再来思考下,是否真的不可以枚举路径来实现路径边权的统计呢。

也许可以:这里给出出题人猜想的一种解法:正确性不详(逃

首先,我们枚举出关于某个点的路径数,对路径进行排序。

如果要一条条去计算路径的长度,显然不行。

所以先思考树上点分治,在进行路径距离更新时,将单条路径算入答案,然后做一个

距离前缀和的计算,处理每条边被经过的次数。也许就能计算了

图的生成树

这题,如果你理解清了题意,并且想到了情况,那么其实也没有那么难。

注意题意是在一开始那棵生成树上变动。一开始理解错了题意,所以难度剧增!

首先,很显然,我按一开始的所有边建一棵以1为根的最小生成树。

考虑加入一条边后的影响:

1:如果这条边已经在一开始的生成树上了,那么显然加入它不变,还是沿用原来的状态。

那么对于这种情况的判断,我们可以并查集去判断。

2:如果这条边不在一开始的生成树上,那么对于u -> lca(u,v)和v – > lca(u,v)上的边,我们可以删除任意一条。

并且,删除某一条边,它的变动点数是它的子节点个数。这点应该很显然。

所以我们的主要目的就是处理出u – >lca(u,v)和v – >lca(u,v)上的最大值满足边权最大(保证最小生成树,显然删最大的边),在边权相同时,满足子节点最多。

所以我们要先处理出每个点的子节点。

首先,如果单纯地dp统计子树,那么,可能这个边不在这个区间内(一开始就是犯了这个错误)

所以我们可以直接遍历这个路径上的所以边,单纯遍历显然过于暴力。

那么我们可以倍增跳,然后预处理出dp[u]i]表示u点向上跳2^i的路径中的最大值(满足边权最大,在边权相同时,满足子节点最多。

然后我们求LCA的同时就可以一并求出这个最值了。

那么,现在需要考虑的就是,是否会跳出区间外。

可以发现,当跳到LCA时,如果去用了dp[LCA][i],那么就不合法了,因为LCA往上显然出了区间。

那么,分析LCA的过程可以发现,当我们LCA跳的时候,是不会用到dp[LCA][i]的。

以上是一种思路:

Code:

// Author: levil
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL,int> pii;
const int N = 1e5+5;
const int M = 1e5+5;
const LL Mod = 199999;
#define rg register
#define pi acos(-1)
#define INF 1e18
#define CT0 cin.tie(0),cout.tie(0)
#define IO ios::sync_with_stdio(false)
#define dbg(ax) cout << "now this num is " << ax << endl;
namespace FASTIO{
    inline LL read(){
        LL x = 0,f = 1;char c = getchar();
        while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
        while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
        return x*f;
    }
    void print(int x){
        if(x < 0){x = -x;putchar('-');}
        if(x > 9) print(x/10);
        putchar(x%10+'0');
    }
}
using namespace FASTIO;
void FRE(){/*freopen("data1.in","r",stdin);
freopen("data1.out","w",stdout);*/}

int n,m,fa[N],tag[N],lg[N],dep[N],f[N][20],ssize[N];
pii dp[N][20];
struct Point{int to,dis;};
vector<Point> G[N];
int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);}
struct Node{int st,to,dis,id;}e[M];
bool cmp1(Node a,Node b){return a.dis < b.dis;}
bool cmp2(Node a,Node b){return a.id < b.id;}
void init()
{
    for(rg int i = 1;i < N;++i) lg[i] = lg[i-1] + ((1<<lg[i-1]) == i);
}
void dfs(int u,int fa)
{
    dep[u] = dep[fa]+1;
    f[u][0] = fa,ssize[u] = 1;
    for(rg int i = 1;i <= lg[dep[u]];++i) f[u][i] = f[f[u][i-1]][i-1];
    for(auto v : G[u]) 
    {
        if(v.to == fa) continue;
        dfs(v.to,u);
        ssize[u] += ssize[v.to];
    }
}
void dfs1(int u,int fa,int dis)
{
    dp[u][0] = pii{dis,ssize[u]};
    for(rg int i = 1;i <= lg[dep[u]];++i) dp[u][i] = max(dp[u][i-1],dp[f[u][i-1]][i-1]);
    for(auto v : G[u]) if(v.to != fa) dfs1(v.to,u,v.dis);
}
pii LCA(int x,int y)
{
    pii res = pii{0,0};
    if(dep[x] < dep[y]) swap(x,y);
    while(dep[x] > dep[y]) 
    {
        res = max(res,dp[x][lg[dep[x]-dep[y]]-1]);
        x = f[x][lg[dep[x]-dep[y]]-1];
    }
    if(x == y) return res;
    for(rg int i = lg[dep[x]]-1;i >= 0;--i)
    {
        if(f[x][i] != f[y][i])
        {
            res = max(res,dp[x][i]);
            res = max(res,dp[y][i]);
            x = f[x][i],y = f[y][i];
        }
    }
    res = max(res,max(dp[x][0],dp[y][0]));
    return res;
}
int main()
{
    init();
    n = read(),m = read();
    for(rg int i = 1;i <= n;++i) fa[i] = i;
    for(rg int i = 1;i <= m;++i) e[i].st = read(),e[i].to = read(),e[i].dis = read(),e[i].id = i;
    sort(e+1,e+m+1,cmp1);
    LL ans = 0;
    for(rg int i = 1;i <= m;++i)
    {
        int u = e[i].st,v = e[i].to,d = e[i].dis;
        int x = Find(u),y = Find(v);
        if(x != y)
        {
            G[u].push_back(Point{v,d});
            G[v].push_back(Point{u,d});
            fa[x] = y,tag[e[i].id] = 1,ans += d;
        }
    }
    sort(e+1,e+m+1,cmp2);
    dfs(1,0);
    dfs1(1,0,0);
    for(rg int i = 1;i <= m;++i)
    {
        if(tag[i]){printf("%lld 0
",ans);continue;}
        pii ma = LCA(e[i].st,e[i].to);
        printf("%lld %lld
",ans-ma.first+e[i].dis,ma.second);
    }
    system("pause");
}
/*
6 8
1 2 10
1 3 20
2 3 20
2 4 30
2 5 60
4 5 70
5 6 40
3 6 80
*/

View Code

可以发现:我们的主要任务就是处理出某段链上的最大值。

显然可以树剖部分,然后线段树维护链上的最值。

可能会有好几段区间,因为不一定连续,写起来稍微麻烦点。

但是这样的思路肯定是对的。代码就不写了.(咕咕咕)

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注