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 ",++caa,ans); } int main() { int ca;scanf("%d",&ca); while(ca--) { solve(); } //system("pause"); return 0; }
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: ",++caa); while(m--) { int L,r;scanf("%d %d",&L,&r); update(L,r,1); printf("%llu ",ans); } } int main() { int ca;scanf("%d",&ca); while(ca--) { solve(); } //system("pause"); return 0; }
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 ",++caa,f ? "NO" : "YES"); } int main() { int ca;scanf("%d",&ca); while(ca--) { solve(); } // system("pause"); return 0; }
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); } } }
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 ",++caa,ans); } int main() { int ca;scanf("%d",&ca); while(ca--) { solve(); } // system("pause"); return 0; }
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 ",++caa,(int)round(ans)); } int main() { int ca;scanf("%d",&ca); while(ca--) { solve(); } //system("pause"); return 0; }