整理

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.
", 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
", 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,mle 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游戏

原文地址:https://www.cnblogs.com/gaozhuoyuan/p/11788772.html