《10.23训练题解》

A - Article

这题看着好做但是挺难的。

dp[i] - 表示打成功了i个字的期望打字数。

dp[i] = dp[i - 1] + 1 * (1 - p) + (dp[i] + 1) * p;

这里并没有考虑x的存档操作,所以如果失败了就要重新打dp[i]的字。

所以就是(dp[i] + 1) * p

化简一下就是dp[i] = (dp[i - 1] + 1) / (1 - p)

我们处理完了这里普通的期望次数后。

可以发现,dp是一个递增的值,所以我们可以分段,考虑每段打了i + x个字后,保存。

然后取一下最小值就好。

// Author: levil
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef long double ld;
typedef pair<int,int> pii;
const int N = 1e5 + 5;
const int M = 2e4 + 5;
const double eps = 1e-10;
const LL Mod = 1e9 + 7;
#define pi acos(-1)
#define INF 1e8
#define IN_INF 0x3f3f3f3f
#define dbg(ax) cout << "now this num is " << ax << endl;
inline long long ADD(long long x,long long y) {return (x + y) % Mod;}
inline long long DEC(long long x,long long y) {return (x - y + Mod) % Mod;}
inline long long MUL(long long x,long long y) {return x * y % Mod;}

double dp[N];//打了i个字的期望次数.
int n,x,caa = 0;
double cal(int k) {
    int c = n / k;
    return 1.0 * k * x + dp[c + 1] * (n % k) + dp[c] * (k - n % k);
}
void solve() { 
    double p;
    cin >> n >> p >> x;
    for(int i = 1;i <= n;++i) {
        dp[i] = (dp[i - 1] + 1) / (1.0 - p);
    }
    double ans = 1000000000000000000;
    for(int i = 1;i <= n;++i) {
        ans = min(ans,cal(i));
    }
    printf("Case #%d: %.6f\n",++caa,ans);
}   
int main() {
    int ca;scanf("%d",&ca);
    while(ca--) {
        solve();
    }
    //system("pause");
    return 0;
}
View Code

B:一个模拟题,队友写了,模拟的神。

D:这题其实不难,观察到模数很特殊,取了一些数试了试,可以发现,最多30次后,a[i]再操作依旧 = a[i]。

那么线段树维护一下就好,注意这里需要稍微优化掉一些不必要的操作,不然容易T.

// Author: levil
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef long double ld;
typedef pair<int,int> pii;
const int N = 1e5 + 5;
const int M = 2e4 + 5;
const double eps = 1e-10;
const LL Mod = 1e9 + 7;
#define pi acos(-1)
#define INF 1e8
#define IN_INF 0x3f3f3f3f
#define dbg(ax) cout << "now this num is " << ax << endl;
inline long long ADD(long long x,long long y) {return (x + y) % Mod;}
inline long long DEC(long long x,long long y) {return (x - y + Mod) % Mod;}
inline long long MUL(long long x,long long y) {return x * y % Mod;}

LL p = 9223372034707292160;
ULL a[N],ans = 0;
int caa = 0;
ULL quick_mul(ULL a,ULL b) {
    ULL re = 0;
    while(b) {
        if(b & 1) re = (re + a) % p;
        a = (a << 1) % p;
        b >>= 1;
    }
    return re;
}
struct Node{int L,r,f;ULL val;}node[N << 2];
void Pushup(int idx) {
    if(node[idx << 1].f && node[idx << 1 | 1].f) node[idx].f = 1;
    node[idx].val = (node[idx << 1].val + node[idx << 1 | 1].val) % p;
}
void build(int L,int r,int idx) {
    node[idx].L = L,node[idx].r = r,node[idx].f = 0;
    if(L == r) {
        if(a[L] == 0 || a[L] == 1) node[idx].f = 1;
        node[idx].val = a[L];
        return ;
    }
    int mid = (L + r) >> 1;
    build(L,mid,idx << 1);
    build(mid + 1,r,idx << 1 | 1);
    Pushup(idx);
}
void update(int L,int r,int idx) {
    if(node[idx].L >= L && node[idx].r <= r && node[idx].f) {
        ans = (ans + node[idx].val) % p;
        return ;
    }
    if(node[idx].L == node[idx].r) {
        ans = (ans + node[idx].val) % p;
        if(node[idx].f) return ;
        ULL pre = node[idx].val;
        node[idx].val = quick_mul(node[idx].val,node[idx].val);
        if(node[idx].val == pre) node[idx].f = 1;
        return ;
    }
    int mid = (node[idx].L + node[idx].r) >> 1; 
    if(mid >= L) update(L,r,idx << 1);
    if(mid < r) update(L,r,idx << 1 | 1);
    Pushup(idx);
}
void solve() { 
    int n,m;scanf("%d %d",&n,&m);
    for(int i = 1;i <= n;++i) scanf("%llu",&a[i]);
    build(1,n,1);
    ans = 0;
    printf("Case #%d:\n",++caa);
    while(m--) {
        int L,r;scanf("%d %d",&L,&r);
        update(L,r,1);
        printf("%llu\n",ans);
    }
}   
int main() {
    int ca;scanf("%d",&ca);
    while(ca--) {
        solve();
    }
    //system("pause");
    return 0;
}
View Code

E:签到

// Author: levil
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef long double ld;
typedef pair<int,int> pii;
const int N = 1e5 + 5;
const int M = 2e4 + 5;
const double eps = 1e-10;
const LL Mod = 1e9 + 7;
#define pi acos(-1)
#define INF 1e8
#define IN_INF 0x3f3f3f3f
#define dbg(ax) cout << "now this num is " << ax << endl;
inline long long ADD(long long x,long long y) {return (x + y) % Mod;}
inline long long DEC(long long x,long long y) {return (x - y + Mod) % Mod;}
inline long long MUL(long long x,long long y) {return x * y % Mod;}
  
int n,caa = 0;
struct Node{int r,e,L;}p[N];
bool cmp(Node a,Node b) {
    return a.e < b.e;
}
void solve() { 
    scanf("%d",&n);
    for(int i = 1;i <= n;++i) cin >> p[i].r >> p[i].e >> p[i].L;
    sort(p + 1,p + n + 1,cmp);
    int now = 0,f = 0;
    for(int i = 1;i <= n;++i) {
        if(now + p[i].r > p[i].e) f = 1;
        now = p[i].e + p[i].L;
    }
    printf("Case #%d: %s\n",++caa,f ? "NO" : "YES");
}   
int main() {
    int ca;scanf("%d",&ca);
    while(ca--) {
        solve();
    }
   // system("pause");
    return 0;
}
View Code

F:这题其实直接推不是很好推,我小数据猜测了一下结论。

然后写了个java大数就过了。

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Scanner;

public class Main {
    public static BigInteger quick_mi(BigInteger a,int b) {
        BigInteger re = new BigInteger("1");
        while(b != 0) {
            if((b & 1) != 0) re = re.multiply(a);
            a = a.multiply(a);
            b >>= 1;
        }
        return re;
    }
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int ca = input.nextInt();
        for(int i = 1;i <= ca;++i) {
            int n = input.nextInt();
            BigInteger ans = quick_mi(new BigInteger("2"),5 * n);
            System.out.print("Case #");
            System.out.print(i);
            System.out.println(": " + ans);
        }

    }
}
View Code

G:这题还是挺不错的,可能状态比较好,看了一会就想到了做法。

首先,贪心选择每条路,每次都选最大价值的那条,然后删掉路上的所有价值。

我的做法是:维护根到每个叶子节点的路径总价值,然后dfs序维护线段树。

可以发现,我们删去一个点的代价就是删去这个点子树内所有点的值,那么做一下子树的区间更新即可。

然后这里我们做一个优化,我们从下往上去删每个点的代价(这里暴力向上就行),然后我们标记一下每个点有没有被删去。

如果一个点被删去了,它往上都肯定被删过,就不用再走了。这样保证了每个点只被更新一下,复杂度就是nlogn。

// Author: levil
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef long double ld;
typedef pair<int,int> pii;
const int N = 1e5 + 5;
const int M = 2e4 + 5;
const double eps = 1e-10;
const LL Mod = 1e9 + 7;
#define pi acos(-1)
#define INF 1e8
#define IN_INF 0x3f3f3f3f
#define dbg(ax) cout << "now this num is " << ax << endl;
inline long long ADD(long long x,long long y) {return (x + y) % Mod;}
inline long long DEC(long long x,long long y) {return (x - y + Mod) % Mod;}
inline long long MUL(long long x,long long y) {return x * y % Mod;}

int v[N],sz[N],dfn[N],rk[N],fa[N],tim = 0,caa = 0;
bool vis[N];
vector<int> G[N];
LL val[N];
void dfs(int u,int ffa) {
    dfn[u] = ++tim;
    fa[u] = ffa;
    rk[tim] = u;
    val[u] = val[ffa] + v[u];
    sz[u] = 1;
    for(auto v : G[u]) {
        dfs(v,u);
        sz[u] += sz[v];
    }
}
void init() {
    tim = 0;
    memset(vis,0,sizeof(vis));
}
struct Node{int L,r;LL mx,tag,pos;}node[N << 2];
void Pushdown(int idx) {
    if(node[idx].tag) {
        node[idx << 1].mx -= node[idx].tag;
        node[idx << 1 | 1].mx -= node[idx].tag;
        node[idx << 1].tag += node[idx].tag;
        node[idx << 1 | 1].tag += node[idx].tag;
        node[idx].tag = 0;
    }
}
void Pushup(int idx) {
    if(node[idx << 1].mx > node[idx << 1 | 1].mx) {
        node[idx].mx = node[idx << 1].mx;
        node[idx].pos = node[idx << 1].pos;
    }
    else {
        node[idx].mx = node[idx << 1 | 1].mx;
        node[idx].pos = node[idx << 1 | 1].pos;
    }
}
void build(int L,int r,int idx) {
    node[idx].L = L,node[idx].r = r,node[idx].tag = 0;
    if(L == r) {
        node[idx].mx = val[rk[L]];
        node[idx].pos = rk[L];
        return ;
    }
    int mid = (L + r) >> 1;
    build(L,mid,idx << 1);
    build(mid + 1,r,idx << 1 | 1);
    Pushup(idx);
}
void update(int L,int r,int val,int idx) {
    if(node[idx].L >= L && node[idx].r <= r) {
        node[idx].mx -= val;
        node[idx].tag += val;
        return ;
    }
    Pushdown(idx);
    int mid = (node[idx].L + node[idx].r) >> 1;
    if(mid >= L) update(L,r,val,idx << 1);
    if(mid < r) update(L,r,val,idx << 1 | 1);
    Pushup(idx);
}
void del(int x) {
    while(1) {
        if(vis[x] == 1 || x == 0) break;
        update(dfn[x],dfn[x] + sz[x] - 1,v[x],1);
        vis[x] = 1;
        x = fa[x];
    }
}
void solve() { 
    init();
    int n,k;scanf("%d %d",&n,&k);
    for(int i = 1;i <= n;++i) scanf("%d",&v[i]),G[i].clear();
    for(int i = 1;i < n;++i) {
        int x,y;scanf("%d %d",&x,&y);
        G[x].push_back(y);
    }
    dfs(1,0);
    build(1,tim,1);
    LL ans = 0;
    for(int i = 1;i <= k;++i) {
        ans += node[1].mx;
        del(node[1].pos);
    }
    printf("Case #%d: %lld\n",++caa,ans);
}   
int main() {
    int ca;scanf("%d",&ca);
    while(ca--) {
        solve();
    }
   // system("pause");
    return 0;
}
View Code

I:队友做出来了,说是找规律加分治,真狠呐。

J:这题我想错方向了。一直在考虑对次数dp。

其实它每次选点和上一次选的是没有概率影响的。

我们可以考虑每个点的概率,p[i][j] - 表示(i,j)至少被选中一次的概率,最后所有加一下就是期望了。

这里我们考虑一下怎么求(i,j)至少被选中一次的概率。很显然容斥。我们算一次都没选中的概率。

我们发现只要两个点在同侧就可以保证不被选中。不过这样四个角落会被重复选中,所以要减去。

// Author: levil
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef long double ld;
typedef pair<int,int> pii;
const int N = 1e5 + 5;
const int M = 2e4 + 5;
const double eps = 1e-10;
const LL Mod = 1e9 + 7;
#define pi acos(-1)
#define INF 1e8
#define IN_INF 0x3f3f3f3f
#define dbg(ax) cout << "now this num is " << ax << endl;
inline long long ADD(long long x,long long y) {return (x + y) % Mod;}
inline long long DEC(long long x,long long y) {return (x - y + Mod) % Mod;}
inline long long MUL(long long x,long long y) {return x * y % Mod;}

double dp[505][505];//dp[i][j] - k次后选中(i,j)的概率
int caa = 0;
double cal(double x,int k) {
    double ans = 1.0;
    for(int i = 1;i <= k;++i) ans = ans * x;
    return ans;
}
void solve() { 
    int m,n,k;scanf("%d %d %d",&m,&n,&k);
    double ans = 0;
    for(int i = 1;i <= n;++i) {
        for(int j = 1;j <= m;++j) {
            LL p = 1LL * n * (j - 1) * n * (j - 1);//全在左侧
            LL p1 = 1LL * n * (m - j) * n * (m - j);//全在右侧
            LL p2 = 1.0 * m * (i - 1) * m * (i - 1);//全在上侧
            LL p3 = 1.0 * m * (n - i) * m * (n - i);//全在下侧
            LL c1 = 1.0 * (i - 1) * (j - 1) * (i - 1) * (j - 1);//左上角
            LL c2 = 1.0 * (n - i) * (j - 1) * (n - i) * (j - 1);//左下角
            LL c3 = 1.0 * (i - 1) * (m - j) * (i - 1) * (m - j);//右上角
            LL c4 = 1.0 * (n - i) * (m - j) * (n - i) * (m - j);//右下角
            LL f = p + p1 + p2 + p3 - c1 -c2 -c3 -c4;
            dp[i][j] = f * 1.0 / (1LL * n * n * m * m);
            ans += 1.0 - cal(dp[i][j],k);
        }
    }
    printf("Case #%d: %d\n",++caa,(int)round(ans));
}   
int main() {
    int ca;scanf("%d",&ca);
    while(ca--) {
        solve();
    }
    //system("pause");
    return 0;
}
View Code

 

posted @ 2021-10-24 10:31  levill  阅读(38)  评论(0编辑  收藏  举报