A.每个状态只有一种后续转移,判断每次转移是否都合法即可。
#include <iostream> #include <cstdio> using namespace std; int a[1001], n; bool check(int x){ if(a[1] == x) return false; int l = a[1], r = x, s = 6-a[1]-x; for(int i = 2; i <= n; i++){ if(a[i] == s) return false; l = a[i]; r = s; s = 6-l-r; } return true; } int main() { cin>>n; for(int i = 1; i <= n; i++) cin>>a[i]; int f = 0; if(check(3)) f = 1; if(f == 1) printf("YES"); else printf("NO"); return 0; }
B.按照题意枚举约数判断即可。
#include <iostream> using namespace std; int n; bool check(int x){ for(int i = 1; i <= 100; i++){ if( ((1<<i) - 1)*(1<<(i-1)) == x) return true; if( ((1<<i) - 1)*(1<<(i-1)) > x) return false; } } int a[1010]; int main() { int ans = 0, temp = 0, last = 0, L; cin>>n; for(int i = 1; i <= n; i++){ if(n%i == 0) if(check(i)) ans = max(ans, i); } cout<<ans<<endl; }
C.先用并查集并起来,然后瞎搞即可。
#include <iostream> #include <cstdio> using namespace std; const int maxn = 1e5 + 100; int a[maxn], f[maxn], v[maxn], s[maxn], M[maxn], x, y; int find(int x) { return x == f[x] ? f[x] : f[x] = find(f[x]); } int main() { long long ans = 0; int n, m; cin>>n>>m; for(int i = 1; i <= n; i++){ scanf("%d", &a[i]); f[i] = i; v[i] = a[i]; } for(int i = 1; i <= m; i++){ scanf("%d %d", &x, &y); f[find(x)] = find(y); } for(int i = 1; i <= n; i++){ v[find(i)] = min(v[find(i)], v[i]); } for(int i = 1; i <= n; i++){ if(M[find(i)] == 0){ M[find(i)] = 1; ans += (long long)v[find(i)]; } } cout<<ans<<endl; }
D.一道题意非常困难的题目orz。
题意大概就是:给你每天的金钱变化,你可以选择加任意数量的钱,但是需要保证每个时刻的钱数不超过d,而且当金钱变化为0时,此时钱的数量必须非负(check一次)
最后还是读错题了。
这道题贪心是对的,先考虑画出价格的折线图。
在第i天加钱,就相当于抬高i天以后的折线。
贪心地想,反正加更多的钱总是更优
所以如果在第i天加钱,就尽量多的加钱
而能加的钱数就是上限d - Max[i](Max为从i到n的后缀),如果比这个还多,那么后面必定会超
所以就在每个需要加钱的位置都贪心地加钱即可。
#include <iostream> #include <cstdio> using namespace std; const int maxn = 1e5 + 100; int a[maxn], n; long long b[maxn], Max[maxn], d; int main() { cin>>n>>d; for(int i = 1; i <= n; i++) scanf("%d", &a[i]); b[0] = 0; for(int i = 1; i <= n; i++) b[i] = b[i-1] + a[i]; Max[n] = b[n]; for(int i = n-1; i >= 1; i--) Max[i] = max(b[i], Max[i+1]); int t = 0, f = 0; if(Max[1] > d){ f = 1; } else { long long h = 0; for(int i = 1; i <= n; i++){ if(a[i] == 0 && b[i] + h < 0){ h = d - Max[i]; t++; if(b[i] + h < 0) { f = 1; break; } } } } if(f){ printf("-1"); } else printf("%d", t); return 0; }
E.给你一个数x,你需要把它拆成y个数的乘积,只要对应位置的yi有一个不同,方案就是不同的,问有多少种方案(负数也算)
首先考虑只有正数。
把x质因数拆分,然后对于每个质因数
你需要把它分配个y个数,比如24 = 3*2^3
其中就需要把3个2分配给y个数,实际上就是y个不同的盒子,3个球,问有多少种方案
答案就是C(y+3-1, y-1)
然后每个质因数乘起来就可以了。
如果是负数,那么其实就是每个正数的方案选出2个变成负数
就是C(n, 0) + C(n, 2) + .... = 2^(n-1)
最后都乘起来就可以了。
这题很蠢的是数组越界了,因为n+m大于1e6,但是没有预处理到(以及找质因数的写法一定要注意)
#include <iostream> #include <vector> #include <cstdio> #define mp make_pair #define fi first #define se second using namespace std; vector< pair<int, int> > V; const int MOD = 1e9 + 7; typedef long long LL; LL f[1100010], invf[1100010]; LL mypow(LL a, LL b){ LL ans = 1; for(; b; b >>= 1, (a *= a) %= MOD) if(b&1) (ans *= a) %= MOD; return ans; } LL C(LL n, LL m){ return f[n]*invf[m]%MOD*invf[n-m]%MOD; } int T, x, y; int main() { cin>>T; f[0] = 1; for(int i = 1; i <= 1100000; i++) f[i] = f[i-1]*i%MOD; invf[1100000] = mypow(f[1100000], MOD-2); for(int i = 1100000-1; i >= 0; i--) invf[i] = (invf[i+1]*(i+1))%MOD; while(T--){ scanf("%d %d", &x, &y); int t = x; long long ans = 1; for(int i = 2; i*i <= x; i++){ if(t % i == 0){ int temp = 0; while(t % i == 0) temp++, t /= i; (ans *= C(temp+y-1, y-1) ) %= MOD; } } if(t != 1) (ans *= y) %= MOD; (ans *= mypow(2, y-1)) %= MOD; printf("%I64d ", ans); } return 0; }
F.给定一棵树,每次询问x构成的子树内,距离x结点小于等于k的点中值最小的是多少。强制在线
这题我是用可持久化线段树+维护dfs序做的,复杂度是nlogn。不过也有很暴力的线段树维护dfs序nlognlogn做法
具体思想就是可持久化线段树的结点对应是dfs序
插入的时间是按深度排序。
那么询问x,k
就是问插入时间从1~deep[x]+k所构成的树中,x的子树中结点最小的值是多少。
然后维护dfs序,就是查询tree(deep[x] + k, ein[x], eout[x])这个区间的值。
大概就是这样,没什么很坑的地方。
#include <iostream> #include <cstdio> #include <cstring> #include <vector> using namespace std; const int maxn = 2e5 + 500; struct Node{ Node *l, *r; int v; void maintain(){ v = min(l->v, r->v); } }pool[maxn*16], *root[maxn], *null; int pooltot = 0; Node *newnode(){ Node* temp = &pool[pooltot++]; temp->l = temp->r = null; temp->v = 1e9; // return temp; } void init(){ null = new Node(); null->l = null->r = null; null->v = 1e9; // } void Insert(Node *&o, Node *o2, int l, int r, int k, int v){ if(o == null) o = newnode(); if(l == r) { o->v = min(v, o2->v); return; } // int mid = (l+r)/2; if(k <= mid) { Insert(o->l, o2->l, l, mid, k, v); o->r = o2->r; } else { Insert(o->r, o2->r, mid+1, r, k, v); o->l = o2->l; } o->maintain(); } int Query(Node *o, int l, int r, int L, int R){ if(o == null) return 1e9; // if(L <= l && r <= R) return o->v; int mid = (l+r)/2, ans = 1e9; // if(L <= mid) ans = min(ans, Query(o->l, l, mid, L, R)); // if(R > mid) ans = min(ans, Query(o->r, mid+1, r, L, R)); // return ans; } int tot = 0, Maxd, timer = 0; int ein[maxn], deep[maxn], eout[maxn], a[maxn], lastd[maxn]; vector<int> D[maxn], G[maxn]; void dfs(int x, int fa, int d){ ein[x] = ++tot; deep[x] = d; Maxd = max(d, Maxd); D[d].push_back(x); for(auto to : G[x]){ if(to == fa) continue; dfs(to, x, d+1); } eout[x] = tot; } int n, r, x, y, q; int main(){ init(); cin>>n>>r; for(int i = 1; i <= n; i++) scanf("%d", &a[i]); for(int i = 1; i < n; i++){ scanf("%d %d", &x, &y); G[x].push_back(y); G[y].push_back(x); } dfs(r, r, 1); for(int i = 0; i <= n + 100; i++) root[i] = newnode(); for(int i = 0; i <= Maxd; i++){ for(auto x : D[i]){ Insert(root[timer+1], root[timer], 1, tot, ein[x], a[x]); timer++; } lastd[i] = timer; } scanf("%d", &q); int last = 0; while(q--){ scanf("%d %d", &x, &y); x = (x+last)%n + 1; y = (y+last)%n; printf("%d ", last = Query(root[lastd[min(Maxd, deep[x]+y)]], 1, tot, ein[x], eout[x])); } }