5690 All X
由于mod一个很小的数,所以把数拆成1111...11*x%k,等价于1%k+10%k+...+1em%k,显然这个东西循环节很小。于是找一下循环节就好了。
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#define xx first
#define yy second
#define LL long long
#define MP make_pair
#define inf 0x3f3f3f3f
#define CLR(a, b) memset(a, b, sizeof(a))
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
using namespace std;
const int maxn = 10100;
int md[maxn], st[maxn];
int main() {
int T, cas = 1;
scanf("%d", &T);
while(T --) {
int x, c, k; LL m;
scanf("%d%I64d%d%d", &x, &m, &c, &k);
int a = 1, cnt = 0;
CLR(st, -1);
int c1, c2;
md[0] = 1;
st[1] = 0;
while(true) {
cnt ++;
a = a * 10 % c;
if(st[a] != -1) {
c2 = cnt;
c1 = st[a];
break;
}
md[cnt] = a;
st[a] = cnt;
}
int ans = 0;
for(int i = 0; i < min(c1 * 1ll, m); i ++) {
ans += md[i];
ans %= c;
}
if(m > c1) {
int tmp = 0;
for(int i = c1; i < c2; i ++) {
tmp = (tmp + md[i]) % c;
}
int cc = c2 - c1;
ans += 1ll * tmp * ((m - c1) / cc) % c;
m = (m - c1) % cc + c1;
for(int i = c1; i < m; i ++) {
ans += md[i];
ans %= c;
}
}
printf("Case #%d:\n", cas ++);
ans = ans * x % c;
puts(ans == k ? "Yes" : "No");
}
return 0;
}
5691 Sitting in Line
dp[i][j][k]表示算到第i个位置,当前状态是j最后一个数是k的最大值是多少。然后发现这样会超时,由于j可以完整的表示出状态来,所以可以把i这层给去掉。于是复杂度16*16*2^16
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#define xx first
#define yy second
#define LL long long
#define MP make_pair
#define INF 0x3f3f3f3f
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1|1
#define CLR(a, b) memset(a, b, sizeof(a))
using namespace std;
int dp[1<<17][17];
int must[17];
int a[17];
int cnt[1<<17];
int main() {
int T, cas = 1;
for(int i = 0; i < (1<<16); i ++) {
cnt[i] = 0;
for(int j = 0; j < 16; j ++) if((1<<j) & i)
cnt[i] ++;
}
scanf("%d", &T);
while(T --) {
int n;
scanf("%d", &n);
CLR(must, -1);
for(int i = 0; i < n; i ++) {
int p; scanf("%d%d", &a[i], &p);
if(p != -1) must[p] = i;
}
for(int j = 0; j < (1<<n); j ++) {
for(int k = 0; k < n; k ++) dp[j][k] = -INF;
}
if(must[0] == -1) {
for(int i = 0; i < n; i ++) {
dp[1<<i][i] = 0;
}
}
else dp[1<<must[0]][must[0]] = 0;
for(int j = 0; j < (1<<n); j ++) {
for(int k = 0; k < n; k ++) if(dp[j][k] != -INF) {
if(must[cnt[j]] == -1) {
for(int s = 0; s < n; s ++) if(((1<<s) & j) == 0) {
dp[(1<<s) | j][s] = max(dp[(1<<s) | j][s], dp[j][k] + a[k] * a[s]);
}
}
else {
int s = must[cnt[j]];
if(((1<<s) & j) == 0) {
dp[(1<<s) | j][s] = max(dp[(1<<s) | j][s], dp[j][k] + a[k] * a[s]);
}
}
}
}
int ans = -INF;
for(int i = 0; i < n; i ++) ans = max(ans, dp[(1<<n)-1][i]);
printf("Case #%d:\n%d\n", cas ++, ans);
}
return 0;
}
5692 Snacks
显然,一个节点u的值,对于其所有儿子节点的影响就是该节点的值。然后修改和查询就很明显了。就是每次修改整个子树或查询子树最大值。然后就可以根据dfs序,把每个子树搞成一个区间。然后线段树随便做做。由于我交的时候还不能用C++,于是手写栈了。
#pragma comment(linker, "/STACK:102400000000,102400000000")
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#define xx first
#define yy second
#define LL long long
#define MP make_pair
#define INF 0x3f3f3f3f
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1|1
#define CLR(a, b) memset(a, b, sizeof(a))
using namespace std;
const int maxn = 100100;
vector<int> G[maxn];
int L[maxn], R[maxn];
int a[maxn];
int dfn = 1;
LL seg[maxn << 2], add[maxn << 2];
void up(int rt) {
seg[rt] = max(seg[rt << 1], seg[rt << 1 | 1]);
}
void down(int rt) {
if(add[rt]) {
add[rt << 1] += add[rt];
seg[rt << 1] += add[rt];
add[rt << 1 | 1] += add[rt];
seg[rt << 1 | 1] += add[rt];
add[rt] = 0;
}
}
void build(int l, int r, int rt) {
seg[rt] = add[rt] = 0;
if(l == r) return ;
int m = (l + r) >> 1;
build(lson);
build(rson);
}
void update(int L, int R, int v, int l, int r, int rt) {
if(L <= l && r <= R) {
add[rt] += v;
seg[rt] += v;
return ;
}
down(rt);
int m = (l + r) >> 1;
if(L <= m) update(L, R, v, lson);
if(R > m) update(L, R, v, rson);
up(rt);
}
LL query(int L, int R, int l, int r, int rt) {
if(L <= l && r <= R) {
return seg[rt];
}
down(rt);
int m = (l + r) >> 1;
LL ret = -1ll * INF * INF;
if(L <= m) ret = max(ret, query(L, R, lson));
if(R > m) ret = max(ret, query(L, R, rson));
return ret;
}
struct Node {
int u, p, i;
Node() {}
Node(int u, int p, int i) : u(u), p(p), i(i) {}
};
void dfs(int u, int p) {
stack<Node> S; S.push(Node(u, p, 0));
dfn = 1;
L[u] = dfn ++;
while(!S.empty()) {
Node u = S.top(); S.pop();
if(u.i == G[u.u].size()) {
R[u.u] = dfn - 1;
continue;
}
u.i ++; S.push(u);
int v = G[u.u][u.i - 1];
if(v == u.p) continue;
L[v] = dfn ++;
S.push(Node(v, u.u, 0));
}
}
/**
void dfs(int u, int p) {
L[u] = dfn ++;
for(int i = 0; i < G[u].size(); i ++) {
int v = G[u][i];
if(v == p) continue;
dfs(v, u);
}
R[u] = dfn - 1;
}
*/
int main() {
int T, cas = 1;
scanf("%d", &T);
while(T --) {
int n, m;
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i ++) {
G[i].clear();
}
for(int i = 1; i < n; i ++) {
int u, v; scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
for(int i = 0; i < n; i ++) {
scanf("%d", &a[i]);
}
dfn = 1;
build(1, n, 1);
// puts("-----");
dfs(0, -1);
for(int i = 0; i < n; i ++) {
update(L[i], R[i], a[i], 1, n, 1);
}
printf("Case #%d:\n", cas ++);
while(m --) {
int op, x; scanf("%d%d", &op, &x);
if(op == 0) {
int y; scanf("%d", &y);
update(L[x], R[x], y - a[x], 1, n, 1);
a[x] = y;
}
else {
printf("%I64d\n", query(L[x], R[x], 1, n, 1));
}
}
}
return 0;
}
5693 D Game
这题比较有意思,其实可以看出,一个任意长度的等差序列,可以拆成长度为2或3个长度。于是问题就变成了找所有长度是2的和长度是3的序列。然后就可以区间dp去做了。判断一个区间是都可行有三种情况
1、该区间[l, r]的两个端点的差在D中,而且[l + 1, r - 1]可以被删掉。
2、该区间[l, r]可以由两个区间[l, k]+[k+1, r] 完整的删掉。
3、该区间可以拆成 l, [l+1, k-1], k, [k+1, r-1], r其中l, k, r是等差的,而且另外两个可以被删掉。
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#define xx first
#define yy second
#define LL long long
#define MP make_pair
#define inf 0x3f3f3f3f
#define CLR(a, b) memset(a, b, sizeof(a))
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
using namespace std;
const int maxn = 333;
LL a[maxn];
bool dp[maxn][maxn];
int dp2[maxn];
map<LL, int> mp;
int main() {
int T;
scanf("%d", &T);
while(T --) {
int n, m;
CLR(dp, false);
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) {
dp[i][i + 1] = 1;
scanf("%I64d", &a[i]);
}
mp.clear();
for(int i = 1; i <= m; i ++) {
int d; scanf("%d", &d);
mp[d] = i;
}
for(int d = 1; d <= n; d ++) {
for(int j = 1; j + d <= n; j ++) {
int i = j + d;
if(mp.count(a[i] - a[j]) && dp[i - 1][j + 1]) {
dp[i][j] = 1;
continue;
}
for(int k = j + 1; k < i; k ++) {
if(dp[i][k] && dp[k - 1][j]) dp[i][j] = true;
if(a[k] + a[k] == a[i] + a[j] && mp.count(a[i] - a[k])) {
if(dp[i - 1][k + 1] && dp[k - 1][j + 1])
dp[i][j] = 1;
break;
}
}
}
}
CLR(dp2, 0);
for(int i = 1; i <= n; i ++) {
dp2[i] = max(dp2[i], dp2[i - 1]);
for(int j = i - 1; j >= 1; j --) if(dp[i][j]) {
dp2[i] = max(dp2[i], dp2[j - 1] + i - j + 1);
}
}
printf("%d\n", dp2[n]);
}
return 0;
}
5694 BD String
首先,对于S[i]来说,他的后半段是由S[i-2]+D+reverse(flip(S[i-2]))组成的(可以把reverse(flip(S[i-1]))化简下),于是就可以根据长度来一点点算B的个数了。
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#define xx first
#define yy second
#define LL long long
#define MP make_pair
#define inf 0x3f3f3f3f
#define CLR(a, b) memset(a, b, sizeof(a))
using namespace std;
const int maxn = 1111;
LL A[maxn], B[maxn], D[maxn];
LL gao(LL n) {
LL ret = 0;
int l = 0;
if(n == 0) return 0;
while(A[l + 1] <= n) l ++;
ret = B[l];
int flip = 1;
n -= A[l];
if(n) n --, ret ++;
l --;
while(l >= 0) {
if(!n) break;
if(A[l] > n) {
flip = 0;
l --; continue;
}
n -= A[l];
ret += B[l];
if(n) {
n --;
if(flip == 0) ret ++;
}
flip = 1;
l --;
}
return ret;
}
int main() {
int T, cas = 1;
A[0] = 1; B[0] = 1; D[0] = 0;
for(int i = 1; i < 1000; i ++) {
A[i] = A[i - 1] * 2 + 1;
B[i] = B[i - 1] + D[i - 1] + 1;
D[i] = B[i - 1] + D[i - 1];
}
scanf("%d", &T);
while(T --) {
LL L, R;
scanf("%I64d%I64d", &L, &R);
printf("%I64d\n", gao(R) - gao(L - 1));
}
return 0;
}
5695 Gym Class
显然这个求最大拓扑序。
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#define xx first
#define yy second
#define LL long long
#define MP make_pair
#define inf 0x3f3f3f3f
#define CLR(a, b) memset(a, b, sizeof(a))
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
using namespace std;
const int maxn = 100100;
vector<int> G[maxn];
int d[maxn];
int main() {
int T, cas = 1;
scanf("%d", &T);
while(T --) {
int n, m;
scanf("%d%d", &n, &m);
CLR(d, 0);
for(int i = 1; i <= n; i ++)
G[i].clear();
for(int i = 1; i <= m; i ++) {
int u, v; scanf("%d%d", &u, &v);
G[u].push_back(v);
d[v] ++;
}
priority_queue<int> Q;
int mm = n + 1;
for(int i = 1; i <= n; i ++) {
if(d[i] == 0) Q.push(i);
}
LL ans = 0;
while(!Q.empty()) {
int u = Q.top(); Q.pop();
mm = min(u, mm);
ans += mm;
for(int i = 0; i < G[u].size(); i ++) {
int v = G[u][i];
d[v] --;
if(d[v] == 0) Q.push(v);
}
}
printf("%I64d\n", ans);
}
return 0;
}