HGOI 20200723

最后一天夏令营了(仅仅是提高组结束,后面还有冲省队。完蛋)

A:棋盘

送分,人均100

对于每行每列

如果数量 \(\ge k\) 那么肯定不行
如果数量 \(=0\) 直接答案加 \(1\)
否则对 \(a[l\)$r][j-k+1$\(j]\) 的这个矩形进行一个加操作,对旁边两个减
重复的加

然后求一遍前缀和

就过了,\(O(n^2)\)

#include <bits/stdc++.h>
using namespace std ;

#define rep(i, a, b) for (int i = a; i <= b; i++)
#define per(i, a, b) for (int i = a; i >= b; i--)
#define siz(a) (int) a.size()
#define pb push_back
#define ll long long
#define fi first
#define se second

const int N = 2010;

int n, k, ans, Ans ;
char mp[N][N] ;
int res[N][N] ;

void add(int X1, int Y1, int X2, int Y2) {
	++res[X1][Y1] ; --res[X1][Y2 + 1] ;
	--res[X2 + 1][Y1] ; ++res[X2 + 1][Y2 + 1] ;
}

signed main() {
	scanf("%d%d", &n, &k) ;
	rep(i, 1, n) scanf("%s", mp[i] + 1) ;
	rep(i, 1, n) {
		int lef = 1, rig = n, label = 0 ;
		rep(j, 1, n) 
		if (mp[i][j] == 'B') {
			lef = max(lef, j - k + 1) ; 
			rig = min(rig, j) ;
			label = true ; 
		}
		if (!label) {
			ans++ ;
			continue ; 
		}
		if (lef <= rig) add(max(i - k + 1 , 1), lef, i, rig);
	}
	rep(j, 1, n) {
		int lef = 1, rig = n, label = 0 ;
		rep(i, 1, n) {
			if (mp[i][j] == 'B') {
				lef = max(lef, i - k + 1) ;
				rig = min(rig, i) ;
				label = true ; 
			}
		}
		if (!label) {
			ans++ ;
			continue ;
		}
		if (lef <= rig) add(lef, max(1, j - k + 1), rig, j) ;
	}
	rep(i, 1, n)
	rep(j, 1, n)
	res[i][j] = res[i][j - 1] + res[i - 1][j] - res[i - 1][j - 1] + res[i][j] ;
	rep(i, 1, n)
	rep(j, 1, n)
	Ans = max(Ans, ans + res[i][j]) ;
	printf("%d\n" , Ans);
	return 0;
}

B:序列

T2

一眼dp

\(dp[i] = dp[j]+ calc(j+1,i)\)

\(calc(l,r)\) 表示 \(l\)~\(r\) 的极差

查询可以用ST表O(1)解决

可得60分

80分:发现决策单调,记录一个 \(p\) 即可

莫名挂到70但告诉我只挂了2个点。无语

70分现场代码:

#include <bits/stdc++.h>
using namespace std;

#define rep(i, a, b) for (int i = a; i <= b; i++)
#define per(i, a, b) for (int i = a; i >= b; i--)
#define siz(a) (int)a.size()
#define pb push_back
#define mp make_pair
#define ll long long
#define fi first
#define se second
#define Log2(n) (int) (log((double) (n)) / log(2.0))

const int mod = 1073741824 ;
const int N = 500020 ;

int n ;
int a[N], Max[N][25], Min[N][25] ;
ll dp[N] ;

namespace READ {
int x, y, z, m ;
int b[N], p[N], l[N], r[N] ;
void read() {
	scanf("%d%d%d%d%d%d", &x, &y, &z, &b[1], &b[2], &m) ;
	rep(i, 1, m) scanf("%d%d%d", &p[i], &l[i], &r[i]) ;
	rep(i, 3, n) b[i] = (1ll * x * b[i - 1] + 1ll * y * b[i - 2] + z) % mod ;
	p[0] = 0 ;
	rep(i, 1, n) {
		rep(j, p[i - 1] + 1, p[i]) a[j] = (b[j] % (r[i] - l[i] + 1) + l[i]) ; 
	} 
}

}

void rmq() {
	rep(i, 1, n) Max[i][0] = a[i] ;
	rep(j, 1, Log2(n + 1))
	rep(i, 1, n + 1 - (1 << j)) 
	Max[i][j] = max(Max[i][j - 1], Max[i + (1 << (j - 1))][j - 1]) ;
	
	rep(i, 1, n) Min[i][0] = a[i] ;
	rep(j, 1, Log2(n + 1))
	rep(i, 1, n + 1 - (1 << j)) 
	Min[i][j] = min(Min[i][j - 1], Min[i + (1 << (j - 1))][j - 1]) ;
}

int ask(int l, int r) {
	if (l > r) return 0 ;
	int k = Log2(r - l + 1) ;
	return max(Max[l][k], Max[r - (1 << k) + 1][k]) - min(Min[l][k], Min[r - (1 << k) + 1][k]) ;
} 

signed main() {
	scanf("%d", &n) ;
	READ::read() ;
	rep(i, 1, n) printf("%d ", a[i]) ; cout << endl ;
	rmq() ;
	if (n <= 5000) {
		dp[0] = 0 ;
		rep(i, 1, n) {
			dp[i] = 0 ;
			rep(j, 0, i - 1) dp[i] = max(dp[i], dp[j] + (ll) ask(j + 1, i)) ;
		}
	} else {
		dp[0] = 0 ;
		int p = 0 ;
		rep(i, 1, n) {
			dp[i] = 0 ;
			rep(j, p, i - 1) 
			if (dp[i] < dp[j] + (ll) ask(j + 1, i)){
				dp[i] = dp[j] + (ll) ask(j + 1, i) ;
				p = j - 1 ;
			}
		} 
	}
	printf("%lld\n", dp[n]) ;
	return 0 ;
}

100分:

瓶颈在 \(ST\) 表的预处理上,显然没有 \(O(n),O(1)\) 的数据结构,因此只能换思路

发现一个区间,如果存在一个极值,那么断开可能变大
如果随便断开答案不可能变大
因此只需对极值的情况考虑,而且只需要记录前一个 \(dp\) 的值就行了,空间时间两边都优化

#include <bits/stdc++.h>
using namespace std ;

#define rep(i, a, b) for (int i = a; i <= b; i++)
#define per(i, a, b) for (int i = a; i >= b; i--)
#define siz(a) (int) a.size()
#define pb push_back
#define mp make_pair
#define ll long long
#define fi first
#define se second

const int mod = 1073741824 ;
const int iinf = 0x3f3f3f3f ;
const ll linf = 0x3f3f3f3f3f3f3f3f ;
const int N = 10000020 ;

int n ;
int a[N] ;
ll ans[2] ;

namespace READ {
int x, y, z, m ;
int b[N], p[N], l[N], r[N] ;
void read() {
	scanf("%d%d%d%d%d%d", &x, &y, &z, &b[1], &b[2], &m) ;
	rep(i, 1, m) scanf("%d%d%d", &p[i], &l[i], &r[i]) ;
	rep(i, 3, n) b[i] = (1ll * x * b[i - 1] + 1ll * y * b[i - 2] + z) % mod ;
	p[0] = 0 ;
	rep(i, 1, n) {
		rep(j, p[i - 1] + 1, p[i]) a[j] = (b[j] % (r[i] - l[i] + 1) + l[i]) ; 
	} 
}
}

signed main() {
	scanf("%d", &n) ;
	READ::read() ;
//	rep(i, 1, n) printf("%d ", a[i]) ; cout << endl ;
	ans[0] = -linf, ans[1] = 0 ;
	int Max = 0, Min = iinf ;
	int p = n ;
	rep(i, 1, n) {
		if (i > 1 && i < n && ((a[i - 1] < a[i] && a[i] >= a[i + 1]) 
						   || (a[i - 1] > a[i] && a[i] <= a[i + 1]))) { 
			ll nxt[2] = {-linf, -linf} ; 
			nxt[0] = max(nxt[0], ans[0] + max(Max, a[p]) - min(Min, a[p])) ;
			if (p != i - 1) nxt[0] = max(nxt[0], ans[1] + Max - Min) ;
			else nxt[0] = max(nxt[0], ans[1]) ;
			nxt[1] = max(nxt[1], ans[0] + max(Max, max(a[p], a[i])) - min(Min, min(a[p], a[i]))) ;
			nxt[1] = max(nxt[1], ans[1] + max(Max, a[i]) - min(Min, a[i])) ;
			ans[0] = nxt[0], ans[1] = nxt[1] ;
			Max = 0, Min = iinf, p = i ;
		} else {
			Max = max(Max, a[i]) ;
			Min = min(Min, a[i]) ;
		}
	//	cout << Max << " " << Min << " " << p << " " << ans[0] << " " << ans[1] << endl ;
	}
	printf("%lld\n", max(ans[0] + max(a[p], Max) - min(a[p], Min), ans[1] + Max - Min)) ;
}

C:游戏

这个题太难了,打个暴力都贼难

暴力:

如果u和v不连通或u与x不连通或v与x不连通那么必定必能经过

这种情况特殊处理

然后发现 \((u,v)\) 方案数 \(=u\)\(x\)\(u\) 合法点数 * \(x\)\(v\)\(v\) 合法点数

合法难求,求不合法的

也就是在路径上有环的

\(Tarjan\) 缩点求出哪些点有环

计算即可

这样的算法在没有修改的情况下是很快的

但是有了修改就GG

然后之后的就一脸懵了

搬题解

std :

#include <bits/stdc++.h>
using namespace std;
int read() {
    char c = getchar();
    while (c != '-' && !isdigit(c)) c = getchar();
    int neg = 0;
    if (c == '-')
        neg = 1, c = getchar();
    int num = 0;
    while (isdigit(c)) num = num * 10 + c - '0', c = getchar();
    return neg ? -num : num;
}
int head[200001], ver[5000001], nxt[5000001], sz;
void addedge(int u, int v) { ver[++sz] = v, nxt[sz] = head[u], head[u] = sz; }
int backup[200001], backupsz;
int cnt, rt;
int dfn[200001], low[200001], now;
int stk[200001], top;
int vis[200001], scc[200001], size[200001], num;
void tarjan(int x) {
    dfn[x] = low[x] = ++now;
    stk[++top] = x;
    vis[x] = 1;
    for (int i = head[x]; i; i = nxt[i])
        if (!vis[ver[i]])
            tarjan(ver[i]), low[x] = min(low[x], low[ver[i]]);
        else if (vis[ver[i]] == 1)
            low[x] = min(low[x], dfn[ver[i]]);
    if (dfn[x] == low[x]) {
        ++num;
        while (stk[top] != x) {
            vis[stk[top]] = 2;
            scc[stk[top--]] = num;
        }
        vis[x] = 2, scc[x] = num, --top;
    }
}
vector<int> e1[200001], e2[200001];
int first[100001], maxlen;
int x[100001], l[100001], r[100001], last;
int single[200001], t1[200001], t2[200001];
int n, x0;
void link(int x0, int l0, int r0, int len) {
    if (l0 > r0)
        return;
    if (r0 - l0 + 1 < len) {
        link(x0, l0, r0, len >> 1);
        return;
    }
    int l1 = (l0 - 1) / len + 1;
    if (len * (l1 - 1) + 1 == l0)
        addedge(x0, first[len] + l1), link(x0, len * l1 + 1, r0, len);
    else
        link(x0, l0, len * l1, len), link(x0, len * l1 + 1, r0, len);
    return;
}
long long check(int mid) {
    memcpy(backup, head, sizeof(backup));
    backupsz = sz;
    for (int i = last + 1; i <= mid; i++) link(x[i], l[i], r[i], maxlen);
    memset(vis, 0, sizeof(vis)), memset(size, 0, sizeof(size)), now = num = 0;
    for (int i = 1; i <= cnt; i++)
        if (!vis[i])
            tarjan(i);
    for (int i = 1; i <= num; i++) single[i] = 0;
    for (int i = 1; i <= cnt; i++) e1[i].clear(), e2[i].clear();
    for (int i = 1; i <= n; i++) single[scc[i]] = 1, ++size[scc[i]];
    for (int i = 1; i <= cnt; i++)
        for (int j = head[i]; j; j = nxt[j]) {
            int u = scc[i], v = scc[ver[j]];
            if (u != v)
                e1[u].push_back(v), e2[v].push_back(u);
            if (i <= n && ver[j] <= n && u == v)
                single[u] = 0;
        }
    memset(t1, 0, sizeof(t1)), memset(t2, 0, sizeof(t2));
    t1[scc[x0]] = t2[scc[x0]] = 1;
    int cnt1 = 0, cnt2 = 0, c1 = n, c2 = n;
    for (int i = 1; i <= num; i++)
        if (t2[i]) {
            c2 -= size[i];
            for (int j = 0; j < e2[i].size(); j++) t2[e2[i][j]] = 1;
        }
    for (int i = 1; i <= num; i++)
        if (single[i] || !size[i])
            t2[i] = 0;
    for (int i = 1; i <= num; i++)
        if (t2[i]) {
            cnt2 += size[i];
            for (int j = 0; j < e2[i].size(); j++) t2[e2[i][j]] = 1;
        }
    for (int i = num; i >= 1; i--)
        if (t1[i]) {
            c1 -= size[i];
            for (int j = 0; j < e1[i].size(); j++) t1[e1[i][j]] = 1;
        }
    for (int i = num; i >= 1; i--)
        if (single[i] || !size[i])
            t1[i] = 0;
    for (int i = num; i >= 1; i--)
        if (t1[i]) {
            cnt1 += size[i];
            for (int j = 0; j < e1[i].size(); j++) t1[e1[i][j]] = 1;
        }
    return 1ll * (n - cnt1) * (n - cnt2) + 1ll * cnt1 * c2 + 1ll * cnt2 * c1;
}
void reset() {
    memcpy(head, backup, sizeof(head));
    sz = backupsz;
}
int main() {
    int m, q;
    long long k;
    cin >> n >> m >> x0 >> q >> k;
    for (int i = 1; i <= m; i++) {
        int u, v;
        u = read(), v = read();
        addedge(u, v);
    }
    cnt = n, maxlen = 1;
    for (int i = 2; i <= n; i <<= 1) {
        first[i] = cnt, maxlen = i;
        for (int j = 1; j + i - 1 <= n; j += i) {
            ++cnt;
            for (int k = j; k < j + i; k++) addedge(cnt, k);
        }
    }
    for (int i = 1; i <= q; i++) x[i] = read(), l[i] = read(), r[i] = read();
    int l0 = 0, r0 = q + 1;
    while (l0 < r0) {
        int mid = (l0 + r0 + 1) / 2;
        if (check(mid - 1) >= k)
            l0 = mid, last = mid - 1;
        else
            r0 = mid - 1, reset();
    }
    cout << l0 << endl;
}
posted @ 2020-07-23 07:59  harryhqg  阅读(164)  评论(0编辑  收藏  举报