CF433 & 434 题解

比赛链接:https://codeforces.com/contest/434
中国人出的浓度很高的一场
kitahara haruki - 北原春希(WA2)
Kuriyama Marai - 栗山未来(境界的彼方)
Ryouko - 御门凉子(出包王女)
Nanami - 七海千秋(弹丸论破)
Tachibana Kanade - 立华奏(ab)
Furukawa Nagisa - 古河渚(cl)

1E 先坑了

2A 2B
水题

// by SkyRainWind
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>

using namespace std;

typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f;
int cnt[4];
signed main(){
	int tot=0,n;scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int x;scanf("%d",&x);x /= 100;
		++ cnt[x];
		tot += x;
	}
	if(tot&1){
		puts("NO");
		return 0;
	}
	tot /= 2;
	for(int i=0;i<=cnt[1];i++){
		int j = tot - i;
		if(j&1)continue;
		j /= 2;
		if(j <= cnt[2]){
			puts("YES");
			return 0;
		}
	}
	puts("NO");

	return 0;
}
// by SkyRainWind
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>

using namespace std;

typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f,maxn=2e5+5;

int n,a[maxn];
LL  sum[maxn], sum2[maxn];

signed main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]), sum[i] = sum[i-1] + a[i];
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)sum2[i] = sum2[i-1] + a[i];
	int m;scanf("%d",&m);
	while(m --){
		int ty,l,r;scanf("%d%d%d",&ty,&l,&r);
		if(ty == 1)printf("%I64d\n",sum[r] - sum[l-1]);
		else printf("%I64d\n",sum2[r] - sum2[l-1]);
	}

	return 0;
}

2C/1A
好像说改中位数就行了
我做麻烦了。。
首先观察到如果要改,那么一定改成这个数对应的很多个邻居中的一个一定最优
然后枚举改成哪个邻居最优,这个用前缀和处理一下就很容易计算了
注意两个相邻数相同的话会出错,注意特判一下

// by SkyRainWind
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>

using namespace std;

typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 2e5 + 5;

vector<int>vi[maxn];
int n,m;
int a[maxn];
LL ans = 0;
LL res[maxn];
LL sum2[maxn],b[maxn];

signed main(){
	scanf("%d%d",&m,&n);
	if(n == 1)return puts("0"),0 ;
	for(int i=1;i<=n;i++)scanf("%d",&a[i]), b[i] = a[i];
	for(int i=1;i<=n-1;i++)ans += abs(a[i] - a[i+1]);
	for(int i=1;i<=n;i++){
		if(i == 1){
			if(a[i]!=a[i+1])vi[a[i]].push_back(a[i+1]); res[a[i]] += abs(a[i] -a[i+1]);
		}
		else if(i == n){
			if(a[i]!=a[i-1])vi[a[i]].push_back(a[i-1]); res[a[i]] += abs(a[i] -a [i-1]);
		}
		else{
			if(a[i]!=a[i+1])vi[a[i]].push_back(a[i+1]);
			if(a[i]!=a[i-1]) vi[a[i]].push_back(a[i-1]);
			 res[a[i]] += abs(a[i]-a[i-1]) + abs(a[i] - a[i+1]);
		}
	}
	LL rr = ans;
	for(int i=1;i<=m;i++)if(vi[i].size()){
		sort(vi[i].begin(), vi[i].end());
		int nvi = vi[i].size();
		for(int j=0;j<nvi;j++)sum2[j+1] = sum2[j] + vi[i][j];
		sum2[nvi + 1] = 0;
		LL r = 1e18;
		for(int j=1;j<=nvi;j++){
			int cur = vi[i][j - 1];
			LL curm = sum2[nvi] - sum2[j] - sum2[j-1] + 1ll * (2*j-nvi-1) * cur;
			r = min(r, curm);
		}
		rr = min(rr, ans + r - res[i]);
	}
	printf("%I64d\n",rr);

	return 0;
}

2D/1B
https://www.luogu.com.cn/blog/DenyTianly/solution-cf433d

2E/1C
多串匹配 联想到AC自动机
又要统计 \([L,R]\) 的个数,数位dp
所以思路就很清晰了:先对 \(n\) 个串建出AC自动机,然后设 \(dp[i][0/1][0/1][acstate][val]\) 表示考虑到第 \(i\) 位,是否顶上界、是否有前导0、AC自动机的结点、当前的权值是 \(val\) 的方案数
每次转移的时候枚举当前的数,然后AC自动机跳,看能匹配到多少个串即可
处理一下特例:有多个前导0的时候,AC自动机不跳
时间复杂度\(O(m \times len \times \sum{l_i})\)

// by SkyRainWind
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>

using namespace std;

typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 10005,mod=1e9+7;

int n,m,k;
int dp[205][2][2][205][505];	// !!!!! bigger
int l0, r0, le[maxn], ri[maxn], num[maxn];

struct AC{
	int lst[maxn];
	int tr[maxn][27],cnt;
	int val[maxn];	// 多少个以 i 结点结尾的单词 
	int fail[maxn];
	
	AC(){cnt=0;memset(val,0,sizeof val);}
	
	void insert(int *s, int ns,int v){
		int p=0;
		for(int i=1;i<=ns;i++){
			int k = s[i];
			if(!tr[p][k])tr[p][k] = ++ cnt;
			p = tr[p][k];
		}
		val[p] += v;
	}
	
	void build(){
		queue<int>Q;
		Q.push(0);
		while(!Q.empty()){
			int u = Q.front();Q.pop();
			for(int i=0;i<26;i++)
				if(tr[u][i]){
					fail[tr[u][i]] = u ? tr[fail[u]][i] : 0;
					if(val[fail[tr[u][i]]])lst[tr[u][i]] = fail[tr[u][i]];
					else lst[tr[u][i]] = lst[fail[tr[u][i]]];
					
					Q.push(tr[u][i]);
				}else tr[u][i] = tr[fail[u]][i];
		}
	}
	
	int query(int *t,int nt){
		int p=0, res=0;
		for(int i=1;i<=nt;i++){
			p = tr[p][t[i]];
			if(val[p])res += val[p];
			int v = lst[p];
			while(v){
				if(val[v])res += val[v];
				v = lst[v];
			}
		}
		return res;
	}
	
	int query(int acst){
		int p=acst, res=0;
		if(val[p])res += val[p];
		int v = lst[p];
		while(v){
			if(val[v])res += val[v];
			v = lst[v];
		}
		return res;
	}
}ac;

int da[10005], dlen;
// lim==1 在上界 lead0==1 有前导零 
int dfs(int cur,int lim,int lead0,int acst,int val){
	int &dd = dp[cur][lim][lead0][acst][val];
	if(cur == dlen + 1){
		if(lead0 == 1)return 0;
		return dd = 1;
	}
	if(~dd)return dd;
	
	int up = lim ? da[cur] : m-1;
	int res = 0;
	for(int i=0;i <= up;i++){
		if(i == 0 && lead0){
			res += dfs(cur+1, 0, 1, acst, val);
			if(res>=mod)res-=mod;
		}else{
			int tt = val + ac.query(ac.tr[acst][i]);
			if(tt <= k){
				res += dfs(cur+1, lim && i == up, 0, ac.tr[acst][i], tt);
				if(res>=mod)res-=mod;
			}
		}
	}
	return dd = res;
}

int solve(int *a,int len){
	memset(dp, -1, sizeof dp);
	for(int i=1;i<=len;i++)
		da[i] = a[i];
	da[len+1] = 0;
	dlen = len;
	
	int t = dfs(1,1,1,0,0);
	return t;
}

int solve0(int *a,int len){
	return ac.query(a, len) <= k;
}

signed main(){
	scanf("%d%d%d",&n,&m,&k);
	scanf("%d",&l0);
	for(int i=1;i<=l0;i++)scanf("%d",&le[i]);
	scanf("%d",&r0);
	for(int i=1;i<=r0;i++)scanf("%d",&ri[i]);
	
	for(int i=1;i<=n;i++){
		int ns;scanf("%d",&ns);
		for(int j=1;j<=ns;j++)
			scanf("%d",&num[j]);
		int v;scanf("%d",&v);
		ac.insert(num, ns, v);
	}
	ac.build();
	printf("%d\n",(mod + (solve(ri,r0) - solve(le,l0))%mod + solve0(le,l0))%mod);

	return 0;
}

1D
考虑网络流做法。
对于所有 \(x\) ,将区间 \([l_x, r_x+1]\) 中的所有整数拆出来,\(l_x\)\(S\) 连接, \(r_x+1\)\(T\) 链接.\(l_x\)\(l_x+1\) 连接的就是 \(f(l_x)\)
考虑如何限制?\(x_u \leq x_v + d\) <=> \(x_v \geq x_u - d\),因此对于所有 \(x_u\) 可能的取值 \(i\),如果 \(i-d\)\(x_v\) 的范围里面,\((u,i)\)\((v,i-d)\) 就连一条inf的边,代表如果选择了 \(x_u\)\(i\) 取值,就必须要选 \(x_v\)\(i-d\) 以上的值(否则就无法构成割了。为什么不能同一个x割多条边?因为割一条边显然不会更差)
image

S 连出去的 和 连入 T 的容量都是inf,然后由于要求的是最大值,因此容量取反再加一个很大的数big,最后 \(big*n - maxflow\) 即可

// by SkyRainWind
#include <cstdio>
#include <vector>
#include <cassert> 
#include <cstring>
#include <iostream>
#include <algorithm>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
#define pii pair<int,int>
 
using namespace std;
 
typedef long long LL;
#define int LL
 
const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 1e6 + 5;
 
int a[maxn], b[maxn], c[maxn];
int n,m,L[maxn],R[maxn];
 
struct ed{
	LL from,to,cap,flow,rev;
	ed(){}
	ed(LL from,LL to,LL cap,LL flow,LL rev):from(from),to(to),cap(cap),flow(flow),rev(rev){}
};
vector<ed>g[maxn];
 
struct netflow{
	int cur[maxn]; 
	int d[maxn], q[20000005], hd, tl;
	int s, t;	// 源 汇 
	
	netflow(){s=t=-1;}
	
	void init(int s0,int t0){
		s = s0, t = t0;
	}
 
	void add(int x,int y,LL v){
		g[x].push_back(ed(x,y,v,0,g[y].size()));
		g[y].push_back(ed(y,x,0,0,g[x].size() - 1));
	}
	
	int bfs(){
		memset(d,0, sizeof d);
		hd = tl = 0;
		q[tl ++] = s;
		d[s] = 1;
		while(hd != tl){
			int now = q[hd ++];
			for(int i = 0;i<g[now].size();i++){
				ed &e = g[now][i];
				if(!d[e.to] && e.cap > e.flow)d[e.to] = d[now] + 1, q[tl ++] = e.to;
			}
		}
		return d[t];
	}
	
	LL dfs(int now,LL rem){	// rem 当前流量 
		if(now == t || !rem)return rem;
		LL flow = 0;
		for(int &i = cur[now]; i < g[now].size();i ++){
			ed &e = g[now][i];
				// 分层图 & 残量不为0 
			if(d[e.to] == d[now] + 1 && e.cap > e.flow){
				LL f = dfs(e.to, min(rem, e.cap - e.flow));
				rem -= f, flow += f, e.flow += f, g[e.to][e.rev].flow -= f;
			}
			if(!rem)break;
		}
		if(rem)d[now] = -1;
		return flow;
	}
	
	LL dinic(){
		assert(s!=-1);
		LL flow = 0;
		while(bfs()){
			memset(cur, 0, sizeof cur);
			flow += dfs(s, 0x3f3f3f3f3f3f3f3f);
		}
		return flow;
	}
}nf;
 
int f(int i,int j){
	return a[i] * j * j + b[i] * j + c[i];
}
 
signed main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
	}
	int S = (n+1) * 202, T = (n+1) * 202 + 1, big = 1000000000000;
	nf.init(S, T);
	for(int i=1;i<=n;i++){
		int l, r;scanf("%lld%lld",&l,&r);
		nf.add(S, l + (i) * 202, 0x3f3f3f3f3f3f3f3f);
		for(int j=l;j<=r;j++){
			nf.add(j + (i) * 202, j + 1 + (i) * 202, big - f(i, j));
		}
		nf.add(r + 1 + (i)*202, T, 0x3f3f3f3f3f3f3f3f);
		L[i] =l, R[i] = r;
	}
	for(int i=1;i<=m;i++){
		int u,v,d;scanf("%lld%lld%lld",&u,&v,&d);
		for(int j=L[u];j<=R[u]+1;j++){	// x_v >= x_u - d j = u
			int xv = j - d;
			if(xv >= L[v] && xv <= R[v] + 1){
				nf.add(j + (u) * 202, xv + (v) * 202, 0x3f3f3f3f3f3f3f3f);
			}
		}
	}
	printf("%lld\n",1ll * big * n - nf.dinic());
 
	return 0;
}
posted @ 2022-11-22 11:37  SkyRainWind  阅读(78)  评论(0编辑  收藏  举报