CSP-S模拟 19/10/12

困难的图
法1:缩点双联通,如果边数正好等于点数,说明是一个简单环
法2: d f s dfs dfs出一棵树,考虑非树边的影响,如果有一条边被两条非树边覆盖,那么不走这条边,那两条非树边会连成另一个环,如果非树边去覆盖树边的话,简单环存在当且仅当这一条链只被经过一次
差分一下一条边经过几次即可,非树边都是反祖边

#include<bits/stdc++.h>
#define N 1000050
using namespace std;
typedef long long ll;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
int n, m;
int first[N], nxt[N << 1], to[N << 1], w[N << 1], tot;
void add(int x, int y){ nxt[++tot] = first[x], first[x] = tot, to[tot] = y;}
struct edge{ int u, v, in, flg; } e[N];
int dep[N], d[N], fa[N], id[N]; bool vis[N];
void dfs(int u, int f){
	vis[u] = 1;
	for(int i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(vis[t] || t == f) continue;
		dep[t] = dep[u] + 1; fa[t] = u; id[t] = i;
		e[(i + 1) >> 1].in = 1;
		dfs(t, u);
	}
}
void calc(int u, int f){
	vis[u] = 1;
	for(int i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(vis[t] || t == f) continue;
		calc(t, u); d[u] += d[t];
	}
}
void get(int u, int f){
	vis[u] = 1;
	for(int i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(vis[t] || t == f) continue;
		d[t] += d[u]; get(t, u);
	}
}
void able(int v, int u){ while(v^u){ e[(id[v] + 1) >> 1].flg = 1; v = fa[v];} }
int main(){
	freopen("graph.in","r",stdin);
	freopen("graph.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i <= m; i++){
		int x = read(), y = read();
		e[i].u = x, e[i].v = y;
		add(x, y); add(y, x);
	} 
	for(int i = 1; i <= n; i++) if(!vis[i]) dfs(i, 0);
	for(int i = 1; i <= m; i++){
		if(e[i].in) continue;
		int u = e[i].u, v = e[i].v;
		if(dep[u] > dep[v]) swap(u, v);
		++d[v]; --d[u];
	}
	memset(vis, 0, sizeof(vis));
	for(int i = 1; i <= n; i++) if(!vis[i]) calc(i, 0);
	memset(vis, 0, sizeof(vis));
	for(int i = 1; i <= n; i++) if(!vis[i]) get(i, 0);
	for(int i = 1; i <= m; i++){
		if(e[i].in) continue;
		int u = e[i].u, v = e[i].v;
		if(dep[u] > dep[v]) swap(u, v);
		if(dep[v] - dep[u] - (d[v] - d[u]) == 0) able(v, u), e[i].flg = 1;
	}
	int ans = 0;
	for(int i = 1; i <= m; i++) if(e[i].flg) ans ^= i;
	cout << ans; return 0;
} 

中等的字符串
70 p t s 70pts 70pts 就是 AC 自动机的板子, f i , j f_{i,j} fi,j 表示走 i i i 步,到 j j j 的最大值,枚举字符转移
f i + 1 , n x t = m a x ( f i , j + v a l n x t ) f_{i+1,nxt}=max(f_{i,j}+val_{nxt}) fi+1,nxt=max(fi,j+valnxt)
发现总点数 ≤ 200 \le 200 200 n ≤ 1 e 12 n\le 1e12 n1e12 ,这启示我们用矩阵乘
+ , ∗ +,* +, 的矩阵乘改成 m a x , + max,+ max,+ 即可,同样满足结合律

#include<bits/stdc++.h>
#define N 205
using namespace std;
typedef long long ll;
ll read(){
	ll cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
ll n; int m, a[N];
int ch[N][26], fail[N], val[N], tot;
void Mx(ll &a, ll b){ if(a < b) a = b;}
struct matrix{
	ll a[N][N];
	matrix(){ memset(a, -0x3f, sizeof(a));}
	matrix operator * (const matrix &A){
		matrix B; for(int i = 0; i <= tot; i++){
			for(int j = 0; j <= tot; j++){
				for(int k = 0; k <= tot; k++){
					Mx(B.a[i][j], a[i][k] + A.a[k][j]);
				}
			}
		} return B;
	}
};
void ins(int k, string s){
	int len = s.length(), now = 0;
	for(int i = 0; i < len; i++){
		int c = s[i] - 'a'; if(!ch[now][c]) ch[now][c] = ++tot;
		now = ch[now][c];
	} val[now] += a[k];
}
void build(){
	queue<int> q; 
	for(int i = 0; i < 26; i++) if(ch[0][i]) q.push(ch[0][i]);
	while(!q.empty()){
		int x = q.front(); q.pop();
		val[x] += val[fail[x]];
		for(int i = 0; i < 26; i++){
			if(ch[x][i]) fail[ch[x][i]] = ch[fail[x]][i], q.push(ch[x][i]);
			else ch[x][i] = ch[fail[x]][i];
		}
	}
}
matrix power(matrix A, ll b){
	matrix ans; for(int i = 0; i <= tot; i++) ans.a[i][i] = 0;
	for(;b; b>>=1, A = A * A) if(b&1) ans = ans * A; return ans;
}
int main(){
//	freopen("string.in","r",stdin);
//	freopen("string.out","w",stdout);
	n = read(), m = read();
	for(int i = 1; i <= m; i++) a[i] = read();
	for(int i = 1; i <= m; i++){
		string s; cin >> s; ins(i, s);
	} build();
	matrix A;
	for(int i = 0; i <= tot; i++) 
		for(int j = 0; j < 26; j++){
			int nxt = ch[i][j]; A.a[i][nxt] = val[nxt];
		}
	A = power(A, n);
	matrix B; B.a[0][0] = 0; B = B * A;
	ll ans = 0;
	for(int i = 0; i <= tot; i++) ans = max(ans, B.a[0][i]);
	cout << ans; return 0;
}

上网
想到了线段树优化建图,但是不能 k 个点都向线段树上连边
因为 k 个点会把线段树分成 k + 1 k+1 k+1 段,直接连边是 k 2 l o g ( n ) k^2log(n) k2log(n)
考虑到 k 个点向线段树连的都是同样的边,我们可以对每个限制建一个虚点,这 k 个点向虚点连边,边权为0,虚点向线段树连边,边权为1,边权的意义是至少比它大多少,然后拓扑排序即可
兴致勃勃写完后发现,由于有线段树优化建图的存在,没有点度数为 0,然后头秃了一小会儿
后来把程序改成了,虚点向 k 个点连边,线段树向虚点连边,线段树本身从下往上连
这样就存在度数为 0 的点并且度数为 0 的点一定是最小的
然后拓扑排序往后面走,记录每个点至少是多少并更新后面的,如果跟原值冲突(比它大),那么不合法
我是从 0 开始赋权值的,但是题目
”小 L 只记得士兵的防守值是位于 [ 1 , 1 e 9 ] [1,1e9] [1,1e9] 之间的整数"
然后直接 0 分滚蛋,关键是暴力思路跟正解一样,对拍还拍不出来
反思:以后不仅要对拍,还要多读几遍题目,把题目的限制搞得明明白白再开始写

#include<bits/stdc++.h>
#define N 1000050
#define M 6000050
using namespace std;
typedef long long ll;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
int n, s, m, a[N], node, val[N];
int first[N], nxt[M], to[M], w[M], tot;
int du[N];
int nd[N];
void add(int x, int y, int z){ du[y]++; nxt[++tot] = first[x], first[x] = tot, to[tot] = y, w[tot] = z;}
void build(int x, int l, int r){
	if(l == r){ nd[x] = l; return; }
	nd[x] = ++node;
	int mid = (l+r) >> 1; build(x<<1, l, mid); build(x<<1|1, mid+1, r);
	add(nd[x << 1], nd[x], 0); add(nd[x << 1 | 1], nd[x], 0); 
}
void modify(int x, int l, int r, int L, int R, int pos){
	if(L<=l && r<=R){ add(nd[x], pos, 1); return; }
	int mid = (l+r) >> 1;
	if(L<=mid) modify(x<<1, l, mid, L, R, pos);
	if(R>mid) modify(x<<1|1, mid+1, r, L, R, pos); 
}
bool topsort(){
	queue<int> q;  
	for(int i = 1; i <= node; i++) if(!du[i]) q.push(i);
 	int cnt = 0;
	while(!q.empty()){
		int x = q.front(); q.pop(); ++cnt;
		if(val[x] > 1e9) return false;
		if(a[x] && val[x] > a[x]) return false;
		val[x] = max(1, max(val[x], a[x]));
		for(int i = first[x]; i; i = nxt[i]){
			int t = to[i]; val[t] = max(val[t], val[x] + w[i]);
			if(--du[t] == 0) q.push(t);
		}
	} return cnt >= node; // circle 
}
int main(){
//	freopen("web.in","r",stdin);
//	freopen("web.out","w",stdout);
	n = node = read(), s = read(), m = read();
	for(int i = 1; i <= s; i++){
		int p = read(); a[p] = read();
	} build(1, 1, n);
	while(m--){
		int l = read(), r = read(), k = read();
		int pre = l - 1;
		int fake = ++node; // fake position 
		for(int i = 1; i <= k; i++){
			int pos = read();
			add(fake, pos, 0); 
			if(pre + 1 <= pos - 1) modify(1, 1, n, pre + 1, pos - 1, fake);
			pre = pos;
		}
		if(pre + 1 <= r) modify(1, 1, n, pre + 1, r, fake);
	} 
	if(!topsort()) puts("Impossible");
	else{
		puts("Possible");
		for(int i = 1; i <= n; i++) cout << val[i] << " "; 
	} return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值