2023年暑假集训总结/7.3

2023年暑假集训总结/7.3

预估成绩:100+50+40+20=210

实际成绩:100+25+24+25=174

T1房

题意:有n个已知中心和长度且互不重合的区间,问有多少个长度为t的区间恰好与其中一个区间的一个端点相等,且不与所有区间重合

思路&做法:

  签到题,注意到答案上界为2n,只需要依次枚举接在每个区间左右的长度为t的区间是否与其他区间重合即可。

  在考场上只花了10分钟就做出来了,但仍有一些坑点:

    1.给定的区间中点不是按照大小排序的,需要自己排序

    2.左右边界有可能是小数,需要手动乘2或开double

std:

#include <bits/stdc++.h>
typedef long long ll;
using std::min; using std::max;
#define INF 1e18
#define pE() puts("")
#define W(x) write(x)
#define rd() read<ll>()
#define lowbit(x) -x & x
#define pS() putchar(' ')
#define E(i, l, r) for (int i = l; i <= r; ++ i)
template <typename T>
inline T read() {
    T x = 0; bool f = 0; char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-') f = 1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
        x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    return f ? -x : x;
}
template <typename T>
inline void write(T x) {
    if (x < 0) {
        putchar('-');
        x = -x;
    }
    if (x / 10) write(x / 10);
    putchar((x % 10) ^ 48);
}
const int N = 1e5 + 5;
struct Node {
    ll x, a;
    friend bool operator < (Node lhs, Node rhs) {
        return lhs.x < rhs.x;
    }
} house[N];
int n, t;
ll res[N * 2], cnt;
int main() {
    freopen("house.in", "r", stdin);
    freopen("house.out", "w", stdout);
    n = rd();
    t = rd();
    E(i, 1, n) {
        house[i].x = rd() * 2;
        house[i].a = rd() * 2;
    }
    std::sort(house + 1, house + n + 1);
    E(i, 1, n) {
        if (i == 1 || house[i].x - house[i].a / 2 - 2 * t >= house[i - 1].x + house[i - 1].a / 2)
            res[++ cnt] = house[i].x - house[i].a / 2 - t;
        if (i == n || house[i].x + house[i].a / 2 + 2 * t <= house[i + 1].x - house[i + 1].a / 2)
            res[++ cnt] = house[i].x + house[i].a / 2 + t;
    }
    std::sort(res + 1, res + cnt + 1);
    int ans = std::unique(res + 1, res + cnt + 1) - res - 1;
    W(ans);
    return 0;
}

T2车

题意:有一个n个点的环,给定k个询问,每个询问给定一个v,从一个点出发加一次油可以到距离小于v的最远点,求从任一点出发跑完整个环需要加油的最小次数。

思路&做法:

  考场上先胡了一个kn^2的50分做法,剩下的点打了一个随机化,结果没开longlongWA了25分。

  容易证明从一个点出发顺时针和逆时针跑的代价是相等的。

  实际上对于每一个询问,将环断为链再复制一份拼接在后面,就可以双指针O(n)处理处从i开始加一次油能跑到的最远距离,再借此转移出从j到2n所需的加油次数,然后用并查集维护即可。

std:

#include <bits/stdc++.h>
typedef long long ll;
using std::min; using std::max;
#define INF 1e18
#define pE() puts("")
#define W(x) write(x)
#define rd() read<ll>()
#define lowbit(x) -x & x
#define pS() putchar(' ')
#define E(i, l, r) for (int i = l; i <= r; ++ i)
template <typename T>
inline T read() {
    T x = 0; bool f = 0; char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-') f = 1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
        x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    return f ? -x : x;
}
template <typename T>
inline void write(T x) {
    if (x < 0) {
        putchar('-');
        x = -x;
    }
    if (x / 10) write(x / 10);
    putchar((x % 10) ^ 48);
}
const int N = 1e5 + 5;
ll n, k, w[N], sum[N], ans;
ll fa[N], R[N], Sum[N];
int getfa(int x) {
    if (fa[x] == x)
        return x;
    return fa[x] = getfa(fa[x]);
}
int main() {
    freopen("car.in", "r", stdin);
    freopen("car.out", "w", stdout);
    n = rd();
    k = rd();
    E(i, 1, n) {
        w[i] = rd();
        w[i + n] = w[i];
    }
    E(i, 1, 2 * n)
        sum[i] = sum[i - 1] + w[i];
    while (k --) {
        int r = 0;
        ll v = rd();
        bool flag = 0;
        E(i, 1, n)
            if (w[i] > v) {
//                std::cout << w[i] << ' ' << v << '\n';
                flag = 1;
                W(-1); pE();
                break;
            }
        if (flag) continue;
        E(i, 1, 2 * n)
            fa[i] = i;
        E(i, 1, 2 * n) {
            while (sum[r] - sum[i - 1] <= v && r + 1 <= n * 2)
                ++ r;
            R[i] = r;
        }
        Sum[n * 2] = 0;
        for (int i = n * 2 - 1; i; -- i)
            Sum[i] = Sum[R[i]] + 1;
        ans = 1e9;
        E(i, 1, 2 * n - 1) {
            fa[getfa(i)] = getfa(R[i]);
            if (i >= n) {
                int tmp = i - n + 1;
                ans = min(ans, Sum[tmp] - Sum[getfa(tmp)]);
            }
        }
        W(ans); pE();
    }
    return 0;
}

T3家

题意:有一个有n个节点的树,对于一个1-n的排列p,满足pi≠i,定义它的代价为i到pi的距离之和,求出最小或最大代价及其对应的排列

思路&做法:

  考场上先无脑把n≤10的dfs打出来,然后考虑部分分,对于树是一条链求最小值的情况,贪心地将1与2交换,3与4交换,以此类推,若n为奇数则将最后三个点顺次交换即可。对于树是菊花图求最大值的情况,将中心节点找出与随意一个节点交换,其他点也随意交换即可。打完这些我再去考虑一般情况,可以发现当求最大值时是可以模拟退火的,而求最小值时则因数据太过分散不行,考虑贪心地在树上交换,可惜打完模拟退火就没有时间打贪心,模拟退火也超时了,得不偿失。

  考虑正解。

  最小时,那么就是在树上贪心,如果我们从子树到某个点 x,留下来大于等于两 个点还要往上,肯定不优,所以大致就是直接贪心的进行匹配就可以了,具体的,只能 留下一个点去匹配。

  最大时,通过推导重心,只要某个子树内的点都不走到它子树内就是可行的,然后每次选两个距离较大的节点进行匹配就行了。

std:

  这道题理解起来不容易,码量也大(我打了5k左右),还没有调出来,只能暂时放别人的代码。

#include <bits/stdc++.h>
#define int long long
#define pb push_back 
using namespace std;
const int INF=1e6+5;
int n;
namespace S{
    int sz[INF],rt,Max1,ans[INF],res,p[INF];
    vector <int> e[INF],v3[INF];
    void DFS(int x,int fa) {
        sz[x]=1;
        for (int v:e[x]) {
            if (v==fa) continue;
            DFS(v,x);
            sz[x]+=sz[v];
            res+=min(sz[v],n-sz[v]);
        }
    }
    void DFS2(int x,int fa) {
        int Max=0;
        for (int v:e[x]) {
            if (v==fa) continue;
            DFS2(v,x);
            Max=max(Max,sz[v]);
        }
        Max=max(Max,n-sz[x]);
        if (Max<Max1) Max1=Max,rt=x;
    }
    void DFS3(int x,int fa,int t) {
        v3[t].pb(x);
        for (int v:e[x]) {
            if (v==fa) continue;
            DFS3(v,x,t);
        }
    }
    struct _node_queue {
        int pos,dis_v;
        bool operator < (const _node_queue &x) const {
            return x.dis_v>dis_v;
        }
    };
    priority_queue < _node_queue >q;
    void solve() {
        for (int i=1;i<n;i++) {
            int x=0,y=0;cin>>x>>y;
            e[x].pb(y);e[y].pb(x);
        }
        Max1=1e9;
        DFS(1,0);
        DFS2(1,0);
        int cnt=0;
        for (int v:e[rt]) {
            cnt++;
            DFS3(v,rt,cnt);
        }
        for (int i=1;i<=cnt;i++) p[i]=i;
        for (int i=1;i<=cnt;i++) q.push({i,v3[i].size()});
        while (q.size()>1) {
            auto it=q.top();q.pop();
            auto it2=q.top();q.pop();
            int l=it.pos,r=it2.pos;
            p[l]=l;p[r]=r;
            if (v3[p[l]].size() && v3[p[r]].size()) {
                ans[v3[p[l]].back()]=v3[p[r]].back();
                ans[v3[p[r]].back()]=v3[p[l]].back();
                v3[p[l]].pop_back();v3[p[r]].pop_back();
            }
            if (v3[l].size()) q.push({l,v3[l].size()});
            if (v3[r].size()) q.push({r,v3[r].size()});
        }
        if (n%2==0) {
            int l=q.top().pos;
            if (v3[l].size()) {
                ans[v3[l].back()]=rt;
                ans[rt]=v3[l].back();
            }
        }
        else {
            ans[rt]=2;
            ans[ans[2]]=rt;
        }
        cout<<res*2<<"\n";
        for (int i=1;i<=n;i++) cout<<ans[i]<<" ";
        cout<<"\n";
    }
}
namespace S1{
    const int INF=1e6+5;
    struct _node_edge{
        int to_,next_;
    }edge[INF<<1];
    int tot,head[INF],vis[INF],cnt[INF],ans,ans2[INF];
    void add_edge(int x,int y) {
        edge[++tot]={y,head[x]};
        head[x]=tot;
    }
    void DFS(int x,int fa) {
        for (int i=head[x];i;i=edge[i].next_) {
            int v=edge[i].to_;
            if (v==fa) continue;
            DFS(v,x);
        }
        cnt[0]=0;
        for (int i=head[x];i;i=edge[i].next_) {
            int v=edge[i].to_;
            if (v==fa) continue;
            if (vis[v]) cnt[++cnt[0]]=vis[v],ans++;
        }
        cnt[++cnt[0]]=x;
        for (int i=1;i+1<=cnt[0];i+=2)
            ans2[cnt[i]]=cnt[i+1],ans2[cnt[i+1]]=cnt[i];
        if (cnt[0]&1) {
            if (cnt[0]==1) {
                if (x==1) {
                    for (int i=head[x];i;i=edge[i].next_) {
                        int v=edge[i].to_;
                        ans2[x]=ans2[v];ans2[v]=1;
                        break;
                    }
                    ans++;
                }
                else vis[x]=cnt[cnt[0]];
            }
            else {
                ans2[cnt[cnt[0]-2]]=cnt[cnt[0]-1];
                ans2[cnt[cnt[0]-1]]=cnt[cnt[0]];
                ans2[cnt[cnt[0]]]=cnt[cnt[0]-2];
            }
        }
    }
    void solve(){
         for (int i=1;i<n;i++) {    
            int x=0,y=0;cin>>x>>y;
            add_edge(x,y);add_edge(y,x);
        }
        DFS(1,0);
        cout<<ans*2<<"\n";
        for (int i=1;i<=n;i++) cout<<ans2[i]<<" "; cout<<"\n"; 
    }
}
signed main()
{
    // freopen("data.in","r",stdin);
    ios::sync_with_stdio(false);
    int t=0;
    cin>>n>>t;
    if (t==2) S::solve();
    else S1::solve();
    return 0;
}

T4数

题意:给定n、k,问1至n中有多少数的各位数字和等于其乘k之后的各位数字和。

思路&做法:

  对于n≤1e6,可以直接枚举,能拿20分(实际上可以卡到25分),对于更大的情况就需要找规律,发现若k模3不余1的情况下所有满足条件的数都是9的倍数,或许可以进行优化,之后在考场上我就不能推出更多有用的结论。

  正解是数位dp,从低位往高位 dp,记录进位,以及 f(x × k) 和 f(x) 的值,然 后发现不行,那么记录两个差值就好了。

std:

#include <bits/stdc++.h>
#define int long long
typedef long long ll;
using std::min; using std::max;
#define INF 1e18
#define pE() puts("")
#define W(x) write(x)
#define rd() read<ll>()
#define lowbit(x) -x & x
#define pS() putchar(' ')
#define E(i, l, r) for (int i = l; i <= r; ++ i)
template <typename T>
inline T read() {
    T x = 0; bool f = 0; char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-') f = 1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
        x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    return f ? -x : x;
}
template <typename T>
inline void write(T x) {
    if (x < 0) {
        putchar('-');
        x = -x;
    }
    if (x / 10) write(x / 10);
    putchar((x % 10) ^ 48);
}
ll n, k, ans;
ll g[335];
ll f[21][335][1005][3];
signed main() {
    freopen("number.in", "r", stdin);
    freopen("number.out", "w", stdout);
    n = rd();
    k = rd();
    int tmp = n;
    int N = 165;
    while (tmp) {
        g[++ g[0]] = tmp % 10;
        tmp /= 10;
    }
    E(i, 0, 9) {
        int opt = 0;
        if (i < g[1])
            opt = 0;
        else if (i == g[1])
            opt = 1;
        else opt = 2;
        ++ f[1][i * k % 10 - i + N][i * k / 10][opt];
    }
    E(i, 2, g[0])
        E(j, -N, N)
            E(l, 0, 999)
                E(p, 0, 2) {
                    E(p1, 0, 9) {
                        int p2 = p;
                        if (p1 > g[i])
                            p2 = 2;
                        else if (p1 < g[i])
                            p2 = 0;
                        f[i][(p1 * k + l) % 10 - p1 + j + N][(l + p1 * k) / 10][p2] += f[i - 1][j + N][l][p];
                    }
                }
    ll res = 0;
    E(j, -N, N)
        E(l, 0, 999) {
            ll tmp = l, sum = 0;
            while (tmp) {
                sum += tmp % 10;
                tmp /= 10;
            }
            if (j + sum == 0)
                res += f[g[0]][j + N][l][0] + f[g[0]][j + N][l][1];
        }
    W(res - 1);
    return 0;
}
posted @ 2023-07-03 19:38  pigeon160  阅读(26)  评论(0编辑  收藏  举报