整理
Life and death
Day1
模拟赛部分
T1:下降的出现后,之后都填9,注意前缀0
const int maxN = 1e5 + 7;
char s[maxN];
int main() {
scanf("%s",s + 1);
int n = strlen(s + 1);
int now = s[1] - '0';
int pos = 1,tmp = 1;
for(int i = 2;i <= n;++ i) {
int x = s[i] - '0';
if(x > now) {now = x;pos = i;}
else {if(x < now) break;}
if(x == now) tmp = i;
}
if(tmp == n) {
printf("%s",s + 1);
return 0;
}
for(int i = 1;i < pos;++ i) {
printf("%c",s[i]);
}
if(s[pos] != '1') printf("%c",s[pos] - 1);
for(int i = pos + 1;i <= n;++ i) printf("9");
return 0;
}
T2:考虑一个 逆序对 对多少个区间产生贡献。
即答案为\(\sum_{a_i < a_j,i > j} a_i * a_j * j * (n - i + 1)\%(10^{12} + 7)\)
直接用树状数组维护一下.
比较特殊的是模数太大会爆int.
const int mod = 1e12 + 7;
const int maxN = 4e4 + 7;
int a[maxN] , ans,p[100007],b[maxN],s[maxN];
int query(int x) {
int ans = 0;
for(;x;x -= x & -x) ans = (ans + p[x]) % mod;
return ans;
}
const int MAX = 1e5;
void add(int x , int val) {
for(;x <= MAX;x += x & -x) {
p[x] = (p[x] + val) % mod;
}
return ;
}
int mul(int a , int b) {
int ans = 0;
for(int now = a;b;b >>= 1, now = (now + now) % mod) {
if(b & 1) ans = (ans + now) % mod;
}
return ans;
}
signed main() {
freopen("multiplication.in","r",stdin);
freopen("multiplication.out","w",stdout);
int n = gi();
for(int i = 1;i <= n;++ i) s[i] = b[i] = gi();
sort(s + 1,s + n + 1);
for(int i = 1;i <= n;++ i) a[i] = lower_bound(s + 1,s + n + 1,b[i]) - s;
for(int i = 1;i <= n;++ i) {
int tmp = query(n) - query(a[i]);
tmp %= mod;
tmp += mod;
tmp %= mod;
ans = ans + mul(mul(tmp , (n - i + 1)) , b[i]) % mod;
ans %= mod;
add(a[i] , b[i] * i);
}
ans %= mod;
ans += mod;
ans %= mod;
cout << ans;
return 0;
}
T3:
我在考场里的想法:
把矩阵缩小,二维离散化后,用二维树状数组维护。
单点加,区间查询。
然后直接用扫描线求矩阵并。
学弟tzt没有判断矩阵交,直接判断矩阵并 是否等于 最大的矩形就过了。
可能老师造数据的时候没有卡掉这一点。
加入这个矩阵的边时,直接check线段树上的值是否为0就能判交了。
所以大概A掉的做法应该是两遍扫描线。
赛后题目讲解:
gdb调试
Day2
模拟赛部分
T1:大大大大大大模拟
struct player {
char name[16]; int num, score;
} a[2][5][500100];
int n, m, t, p, q, turn[2][5], sum[2][5], psum, qsum, num, score, team, pos;
char sname[16], spos[16];
bool cmp(player a, player b) {
return (a.score == b.score ? a.num < b.num : a.score > b.score);
}
int main() {
freopen("match.in", "r", stdin);
freopen("match.out", "w", stdout);
scanf("%d%d%d%d%d", &n, &m, &t, &p, &q);
for (int i = 1; i <= n + m; i++) {
team = (i <= n ? 0 : 1);
scanf("%s %d %s %d", sname, &num, spos, &score);
if (spos[0] == 'p' && spos[1] == 'g') pos = 0;
if (spos[0] == 's' && spos[1] == 'g') pos = 1;
if (spos[0] == 's' && spos[1] == 'f') pos = 2;
if (spos[0] == 'p' && spos[1] == 'f') pos = 3;
if (spos[0] == 'c') pos = 4;
a[team][pos][++sum[team][pos]] = (player) {"", num, score};
strcpy(a[team][pos][sum[team][pos]].name, sname);
}
for (int i = 0; i <= 4; i++) {
sort(a[0][i] + 1, a[0][i] + sum[0][i] + 1, cmp);
sort(a[1][i] + 1, a[1][i] + sum[1][i] + 1, cmp);
}
for (int i = 0; i <= 4; i++) turn[0][i] = turn[1][i] = 1;
psum = 1, qsum = 1;
while (p * psum < t || q * qsum < t) {
if (p * psum <= q * qsum) team = 0, psum++; else team = 1, qsum++;
int mn = 999, pm; player up, down;
for (int i = 0; i <= 4; i++) {
int k = turn[team][i], c = a[team][i][k].score - a[team][i][k + 1].score;
if (k == sum[team][i]) continue;
if (c < mn || (c == mn && a[team][i][k + 1].num < up.num))
mn = c, pm = i, up = a[team][i][k + 1], down = a[team][i][k];
}
turn[team][pm]++;
char ct = (team == 0 ? 'A' : 'B');
printf("Substitution by %c,No.%d %s is coming up to change No.%d %s.\n", ct, up.num, up.name, down.num, down.name);
}
return 0;
}
T2:挺神的一道题,题目难度偏大。
考场只想到了80\(n(log_2n)^2\)暴力做法
正解是二分+单调性。
二分在上面累计的格子,然后会发现这个东西其实是单调的。
#include<bits/stdc++.h>
#define N 500010
using namespace std;
typedef long long ll;
ll s[N], T;
int x[N], a[N], n;
bool check(ll H) {
ll nowh = 0, tim = 0;
int lc = 0, rc = 0, l = 1, r = n + 1;
for (int i = 1; i <= n; i++)
if (nowh + a[i] <= H) nowh += a[i], tim += (ll)(x[i] - x[1]) * a[i];
else {r = i, rc = H - nowh, tim += (ll)(x[i] - x[1]) * rc; break;}
if (tim <= T) return 1;
for (int i = 2; i <= n; i++) {
ll suml = s[i - 1] - s[l - 1] - lc;
ll sumr = s[r - 1] - s[i - 1] + rc;
tim += (x[i] - x[i - 1]) * (suml - sumr);
while (r <= n && x[i] - x[l] > x[r] - x[i]) {
int can = min(a[l] - lc, a[r] - rc);
tim += (x[r] - x[i] - x[i] + x[l]) * can;
lc += can, rc += can;
if (lc >= a[l]) ++l, lc = 0;
if (rc >= a[r]) ++r, rc = 0;
}
if (tim <= T) return 1;
}
return 0;
}
int main() {
freopen("block.in", "r", stdin);
freopen("block.out", "w", stdout);
scanf("%d%I64d", &n, &T); T /= 2;
for (int i = 1; i <= n; i++) scanf("%d", &x[i]);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i];
ll l = 0, r = s[n], ans;
while (l <= r) {
ll mid = (l + r) >> 1;
if (check(mid)) l = mid + 1, ans = mid;
else r = mid - 1;
}
printf("%I64d\n", ans);
return 0;
}
T3:神仙manacher题不会做
赛后讲解
CF17E Palisection
首先补集转化。转而计算有多少个回文子串不相交
首先处理所有以该点为中心的回文子串能扩展到的最大位置。
然后差分,前缀和。
再前缀和。
处理答案即可。
codeforces 1045B
分析问题的神仙题。
考虑一个数\(z\)怎么才能不被表示出来。
就是对于每一个\(a_i\)都能找到另外一个\(a_j\)使得\((a_i+a_j) % M = z\)
把序列a从小到大排序
如果\(a_i <= Z\),则\(a_j\)一定\(<= M\)
如果\(a_i > Z\),则\(a_j\)一定\(> M\)
这个是可以证明的。
然后把序列分成两部分要保证两部分首位相等,即回文。
这样就可以处理回文串,然后差分加上,最后用前缀和计算答案。
几个简单的小问题
数轴上有n个开区间\((a_i,b_i)\)。选择尽量多个区间,是的这些区间两两没有公共点。
把b_i从小到大排序
一定选择第一个,记录l,不想交能选就选。
数轴上有n个闭区间\([a_i,b_i]\),取尽量少的点,使得每个区间至少有一个点。
按左端点递增顺序排序,如果左端点相同,按右端点递增顺序排序
记录最右点,不行则新增加一个点。
给定n个按长度从大到小排序的区间,取若干个区间使得[1,M]能被覆盖,并要使得最短的区间尽量长。
复杂度要求比\(nlogn\)小
并查集,依次添加区间,f_i表示i能达到的最右边的点,类似于并查集一样维护起来。
添加区间的时候,直接开始跳,如果没有被标记,则连起来。
USACO2005 dec sliver
在n个带权区间中,选择一些区间,覆盖\([1,M]\)所有整数点,求权和的最小值。
\(n \le 10^5,m\le 10^9\)
按照y_i排序,设\(f_i\)表示覆盖完全\([1,y_i]\)的权值和的最小值
若\(y_j >= x_i\)则\(f_i = min(f_i,f_j + w_i)\)
用树状数组维护
codeforces 776C
一个长度为n的序列,给定数k,求有多少个区间的和是\(k^i(i = 0,1,2,3,4,5....)\)
\(n \le 1e5, A_i \le 1e5\)
求一个前缀和,问题转化为\((sum_i - sum_j) \% k == 0\)的方案数
直接用\(f_i\)表示sum_j模k -= i的方案数。然后动态更新答案就行了。
xdoj1079
前缀和水题
bzoj1044
首先二分长度check出总长度最长的一块最小K.
然后设\(f_{i,j}\)表示前i个木棍,
矩阵乘法
忘记矩阵怎么推的了。
入门构造题: poj3735 Training little cats
P3758 [TJOI2017]可乐
矩阵优化DP
设\(f_{i,j}\)表示走了j步到i的方案数.
对于操作1\(f_{i,j} = f_{i,j - 1}\)
对于操作2\(f_{i,j} = f_{v,j - 1}\)
操作3就是对Ans累加这个和
然后我们构造一个矩阵来优化快速幂,就是酱紫。
P3597 [POI2015]WYC
二分第k小路径的长度,然后用矩阵快速幂计算方案数,check是否>= k
计算方案数的时候。拆点,对于长度为2的边,多拆出一个点,对于长度为3的边,多拆除两个点
BZOJ2510 弱题
不会做.
P2044 [NOI2012]随机数生成器
矩阵优化递推。
一些奇奇怪怪的NPC问题:
3-sat
哈密尔顿路(回路
三分图
最大
博弈论
如果一个状态只能转移必胜态,那这个是必败态。
如果一个状态转移出去的有一个是必败态,那么这个状态是必胜态。
这样就可以设状态DP了.
题目:
有两个数x,y = (1,0)。
每次有三种可能的操作。
1.(x,y) -> (1,x+y)
2.(x,y) -> (2x,y)
3.(x,y) -> (3x,y)
给定一个数K.
y >= k.他就输,x + y >= k时,只能使用1操作
k<=5w.
首先考虑朴素的就是\(f_{i}{j}\)表示x=i,y = j是必胜态还是怎么滴。
会发现x只能是\(3^x*2^y\)所以设\(f[i][k][j]\)表示\(x=2^i*3^k y=j\)的状态
SG定理
有n个游戏,问先手必胜,还是先手必败。
SG定理:
必败态的sg值=0
SG[i] = mex(i能转移到的状态的SG值)
n个游戏,一起玩的SG值=每个游戏SG值亦或起来的结果.
经典问题:NIM石子游戏.
N堆石子,第i堆石子的个数是\(a_i\),每次可以对任意一堆石子拿走任意多个石子
两种做法:1.SG定理DP 2.转化为NIM游戏