CCF-CSP 题解

CSP

202203

1 田地丈量

题意:

给定 n n n 块互不相交的矩形,询问 ( 0 , 0 ) ( a , b ) (0,0)(a, b) (0,0)(a,b) 区域内矩形面积和

解析:

先判断矩形跟区域是否有重叠的部分,如果有,求出重叠的部分

代码:
#include
using namespace std;
typedef long long ll;
typedef double db;
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
#define fi first
#define se second
typedef pair<int, int> pii;

ll n, a, b;
ll ans;

ll cal(ll x1, ll y1, ll x2, ll y2){
	x1 = max(x1, 0ll);
	y1 = max(y1, 0ll);
	x2 = min(x2, a);
	y2 = min(y2, b);
	
	ll res = (x2 - x1) * (y2 - y1);
	return res;
}
int main(){	
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n >> a >> b;
	for(int i = 1; i <= n; i++){
		ll x1, x2, y1, y2;
		cin >> x1 >> y1 >> x2 >> y2;
		if(x2 <= 0 || y2 <= 0 || x1 >= a || y1 >= b)
			continue;				
		ans += cal(x1, y1, x2, y2);
	}
	cout << ans;
	return 0;
}


2 垦田计划

题意:

n n n 块土地,每块土地花费 t i t_i ti天,总耗时为最大的时间。有 m m m 资源,在第 i i i 块土地使用 c i c_i ci 资源可以使时间减少一天。询问最少需要多少天

解析:

使最大的 t i t_i ti 最小,可以二分答案。对于当前的时间 x x x ,如果一块土地的时间大于 x x x,则花费资源将时间减少到 x x x花费的资源是否小于 m m m

代码:
#include
using namespace std;
typedef long long ll;
typedef double db;
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
#define fi first
#define se second
typedef pair<int, int> pii;

int n, m, k;
int t[maxn], c[maxn];
int ans;
bool check(int x){
	int cost = 0;
	for(int i = 1; i <= n; i++){
		if(t[i] > x){
			cost += c[i] * (t[i]-x);
		}
	}
	return cost <= m;
}
int main(){	
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n >> m >> k;
	int l = k, r = 0;
	for(int i = 1; i <= n; i++){
		cin >> t[i] >> c[i];
		r = max(r, t[i]);
	}
	
	while(l <= r){
		int mid = (l+r) >>1;
		if(check(mid)){
			r = mid-1; 
			ans = mid;
		}
		else
			l = mid+1;
	}
	cout << ans;
	return 0;
}



3 LDAP

题意

每个用户有若干属性,每个属性有一个值。

m m m 个表达式,选出满足表达式的用户。

40分解析:

直接模拟即可

#include
using namespace std;
typedef long long ll;
typedef double db;
const int maxn = 1e5+10;
const int maxm = 505;

#define fi first
#define se second
typedef pair<int, int> pii;

struct User{
	int id;
	vector<int> s;
	User(){
		s.resize(505);
		for(int i = 0; i < s.size(); i++)
			s[i] = -1;
	}
};
struct Base{
	int op; // 1 为满足 2为不满足 
	int pos, val;
};
struct Expr{
	int op; //0 为原子,1为&,2为|	
	Base expr1;
	Base expr2;		 
};

vector<User> users;
vector<Expr> exprs;
int n, m;
Base exchangeBase(string s){
	int pos = 0;
	for(int i = 0; i < s.size(); i++){
		if(s[i] == ':' || s[i] == '~'){
			pos = i;
			break;
		}
	}
	Base base;
	int x = 0, val = 0;
	for(int i = 0; i < pos; i++)
		x =  x * 10 + s[i]-'0';
	for(int i = pos+1; i < s.size(); i++)
		val = val * 10 + s[i] -'0';
			
	if(s[pos] == ':')
		base.op = 1;
	else	
		base.op = 2;
	base.pos = x;
	base.val = val; 
	return base;	
}
void check(Expr expr){
	vector<int> res;
	res.clear();
	for(int i = 0; i < users.size(); i++){
		User user = users[i];
		if(expr.op == 0){
			if(expr.expr1.op == 1){ // 相等 
				if(user.s[expr.expr1.pos] == expr.expr1.val)
					res.push_back(user.id);
			}
			else if(expr.expr1.op == 2){
				if(user.s[expr.expr1.pos] != -1)
					if(user.s[expr.expr1.pos] != expr.expr1.val)
						res.push_back(user.id);
			}
		}
		else if(expr.op == 1){ // 且
			int flag1 = 0, flag2 = 0; 
			
			if(expr.expr1.op == 1){ // 相等 
				if(user.s[expr.expr1.pos] == expr.expr1.val)
					flag1 = 1;
			}
			else if(expr.expr1.op == 2){
				if(user.s[expr.expr1.pos] != -1)
					if(user.s[expr.expr1.pos] != expr.expr1.val)
						flag1 = 1;
			}
			if(flag1 == 0)
				continue;
			if(expr.expr2.op == 1){ // 相等 
				if(user.s[expr.expr2.pos] == expr.expr2.val)
					flag2 = 1;
			}
			else if(expr.expr2.op == 2){
				if(user.s[expr.expr2.pos] != -1)
					if(user.s[expr.expr2.pos] != expr.expr2.val)
						flag2 = 2;
			}
			
			if(flag1 && flag2)
				res.push_back(user.id);
		}
		else{
			int flag1 = 0, flag2 = 0; 
			
			if(expr.expr1.op == 1){ // 相等 
				if(user.s[expr.expr1.pos] == expr.expr1.val)
					flag1 = 1;
			}
			else{
				if(user.s[expr.expr1.pos] != -1)
					if(user.s[expr.expr1.pos] != expr.expr1.val)
						flag1 = 1;
			}
			if(flag1){
				res.push_back(user.id);
				continue;
			}

			if(expr.expr2.op == 1){ // 相等 
				if(user.s[expr.expr2.pos] == expr.expr2.val)
					flag2 = 1;
			}
			else if(expr.expr2.op == 2){
				if(user.s[expr.expr2.pos] != -1)
					if(user.s[expr.expr2.pos] != expr.expr2.val)
						flag2 = 2;
			}
			if(flag2)
				res.push_back(user.id);
		}
	}
	sort(res.begin(), res.end());
	for(int i = 0; i < res.size(); i++){
		if(i != 0)
			cout << " " << res[i];
		else
			cout << res[i];
	}
	cout << endl;
}
int main(){	
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n;
	for(int i = 1; i <= n; i++){
		User user;
		int id, cnt;
		cin >> id >> cnt;
		for(int j = 1; j <= cnt; j++){
			int x, val;
			cin >> x >> val;
			user.s[x] = val;
		}
		user.id = id;
		users.push_back(user);
	}
	
	
	cin >> m;
	for(int k = 1; k <= m; k++){
		string s;
		cin >> s;
		Expr expr;
		if(s[0] == '&' || s[0] == '|'){
			int len = 0;
			int pos = 0;
			for(int i = 1; i < s.size(); i++){
				if(s[i] == ')'){
					len = i - 2;
					pos = i;
					break;
				}					
			}
			
			int len2 = s.size()-len-5;
			expr.expr1 = exchangeBase(s.substr(2, len));
			expr.expr2 = exchangeBase(s.substr(pos+2, len2));
			if(s[0] == '&')
				expr.op = 1;
			else
				expr.op = 2;
		}
		else{
			expr.expr1= exchangeBase(s);
			expr.op = 0;
		}
		exprs.push_back(expr);
	} 
	
	for(int i = 0; i < exprs.size(); i++){
		check(exprs[i]);
	}
	return 0;
}



5 施肥

题意:

有区间 [ 1 , n ] [1,n] [1,n],有 m m m 辆车,每辆车可以恰好覆盖 [ l i , r i ] [l_i,r_i] [li,ri]。询问有多少二元组 ( L , R ) (L,R) (L,R) 满足 [ L , R ] [L,R] [L,R] 内的点都被覆盖至少一次, [ L , R ] [L,R] [L,R] 之外的点都没有被覆盖

75分解析:

复杂度为 O ( n 2 ) O(n^2) O(n2) 的思路:

容易发现,只有车的端点才有可能对答案产生贡献。

将所有车按右端点升序排序。

对于遍历到的当前车 i i i,统计 L = l i L = l_i L=li 的答案个数。

对于前边的车 k ( k < i ) k(k < i) k(k<i) k k k 一定不会产生贡献,因为 r k ≤ r i r_k \le r_i rkri。对于后边的车 j ( j > i ) j(j>i) j(j>i),如果 l j < l i l_j < l_i lj<li ,则 j j j 不会对答案产生贡献;如果 l j > c u r R + 1 l_j > curR+1 lj>curR+1 j j j 区间连不上当前区间,也不会答案产生贡献。即 l j ≥ l i l_j \ge l_i ljli l j < = c u r R + 1 l_j <= curR+1 lj<=curR+1 j j j 才会产生贡献,并用 r j r_j rj 更新 c u r R curR curR。( c u r R curR curR 为左端点为 l i l_i li 时,能连成的区间右端点最大值)

s e t set set 去掉重复的区间

对于特殊性质 A :区间不会包含

则任意两区间只能是 “相交”,“相离” 的关系。

依然按照右端点排序,预处理每个区间后能连上区间个数 g ( i ) g(i) g(i)

倒序处理。如果区间 i i i 与区间 i + 1 i+1 i+1 相交,则 g ( i ) = g ( i + 1 ) + 1 g(i) = g(i+1)+1 g(i)=g(i+1)+1; 否则 g ( i ) = 0 g(i) = 0 g(i)=0

答案为 ∑ i ( 1 + g ( i ) ) \sum\limits_i(1+g(i)) i(1+g(i)),时间复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn)

代码:
#include
using namespace std;
typedef long long ll;
typedef double db;
const int maxn = 2e5+10;
const int INF = 0x3f3f3f3f;
#define fi first
#define se second
typedef pair<int, int> pii;

struct node{
	int l, r;
	node(){}
	node(int l, int r) : l(l), r(r){}
	bool operator < (const node &b) const{
		if(r == b.r)
			return l < b.l;
		else
			return r < b.r;
	}
	bool operator == (const node &b) const{
		return l == b.l && r == b.r;
	}
};
set<node> se;
int n, m;
//node s[maxn];
vector<node> s;
int g[maxn];
ll ans = 0;
int main(){	
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n >> m;
	for(int i = 1; i <= m; i++){
		//cin >> s[i].l >> s[i].r;
		int l, r;
		cin >> l >>r;
		s.push_back((node){l, r});
	}
	//sort(s+1, s+1+m);
	sort(s.begin(), s.end());
	//s.erase(unique(s.begin(), s.end()), s.end());

	if(n > 3000){
		g[m-1] = 0;
		for(int i = s.size()-2; i >= 0; i--){
			if(s[i].r >= s[i+1].l-1)
				g[i] = g[i+1]+1;
			else
				g[i] = 0;
		}
		
		for(int i = 0; i < m; i++){
			ans += g[i]+1;
		}
		cout << ans << endl;
		return 0;
	}
	for(int i = 0; i < s.size(); i++){
		int curr = s[i].r;
		int res = 1;
		if(se.count(node(s[i].l, s[i].r)) == 0)
			se.insert(node(s[i].l, s[i].r));
		else
			res = 0;			
		for(int j = i+1; j < s.size(); j++){
			if(s[j].l < s[i].l)
				continue;
			if(s[j].l <= curr+1){
				curr = max(curr, s[j].r);
				if(se.count(node(s[i].l, s[j].r)) == 0){
					se.insert(node(s[i].l, s[j].r));
					res++;
				}					
			}
		}
		ans += res;
	}
	
	cout << ans << endl;
	return 0;
}

 


202212

1 现值计算

题目:

CCF-CSP 题解_第1张图片

代码:
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;


int n;
db x, res;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n >> x;
	for(int i = 0; i <= n; i++){
		db y; cin >> y;
		res += y * pow(1 + x, -i);
	}
	cout << setprecision(10) << res << endl;
	return 0;
}


2 训练计划

题意:

一个科目耗时 t i t_i ti,一些科目有前置科目,只有当前置科目学完时,才能开始学当前科目。前置科目的编号一定小于当前科目。

询问每一项科目的最早开始时间。询问在完成的情况下的最晚开始时间,如果不能完成则不输出。

解析:

建图,科目看成节点,依赖关系看成边,前置科目向当前科目连边;对于没有前置科目的科目,0号节点向该节点连边。

在dfs的过程中,当前节点为 u u u,记录 u u u 的深度 d e p u dep_u depu,以及以 u u u 为根的子树的高度 m a x d e p u maxdep_u maxdepu,即 u u u u u u 子树中节点的最大距离。

对节点 u u u ,执行完 u u u 的父亲之后就可以执行 u u u 。因此执行完 u u u 的父亲需要时间 d e p ( f a [ u ] ) dep(fa[u]) dep(fa[u])。从时刻 1 开始执行,所以 u u u 可以开始执行的时间为 1 + d e p ( f a [ u ] ) 1 + dep(fa[u]) 1+dep(fa[u])

首先判断能不能全部执行完,即 m a x d e p 0 maxdep_0 maxdep0 是否大于 n n n

然后对每个节点 u u u,执行完 u u u 子树的时间为 t u + m a x d e p u t_u + maxdep_u tu+maxdepu,所以答案为 n + 1 − t u − m a x d e p u n+1-t_u - maxdep_u n+1tumaxdepu

代码:
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int n, m;
int p[maxn];
int t[maxn];
int head[maxn], tot;
struct edge{
	int to, nxt, w;
}e[maxn];
void add(int a, int b, int c){
	e[++tot].nxt = head[a];
	e[tot].to = b;
	e[tot].w = c;
	head[a] = tot;
}
int dep[maxn], maxdep[maxn], fa[maxn];
void dfs(int u){
	for(int i = head[u]; i; i = e[i].nxt){
		int v = e[i].to;
		int w = e[i].w;
		fa[v] = u;
		dep[v] = dep[u] + w;
		dfs(v);
		maxdep[u] = max(maxdep[u], w + maxdep[v]);
	}
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n >> m;
	for(int i = 1; i <= m; i++)
		cin >> p[i];
	for(int i = 1; i <= m; i++)
		cin >> t[i];
	
	for(int i = 1; i <= m; i++){
		add(p[i], i, t[i]);
	}
	
	dfs(0);	
	for(int i = 1; i <= m; i++)
		cout << 1 + dep[fa[i]]<< " ";
	cout << endl;
	
	if(maxdep[0] > n)
		return 0;
	for(int i = 1; i <= m; i++){
		int res = n - maxdep[i] - t[i] + 1;
		cout << res << " ";
	}
	return 0;

}


3 JPEG 解码

题意:

大模拟

代码:
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
const db pi = acos(-1);
typedef pair<int, int> pii;

int q[10][10];
db m[10][10];
db mres[10][10];
int ans[10][10];
int a[100];

db f(int x){
	if(x == 0)
		return sqrt(0.5);
	else
		return 1;
}

int dx[6] = {0, 0, -1, 1, 1, -1};
int dy[6] = {-1, 1, 0, 0, -1, 1};

int nxt[8][8] = {
{1, 4, 1, 4, 1, 4, 1, 4},
{3, 5, 4, 5, 4, 5, 4, 3},
{5, 4, 5, 4, 5, 4, 5, 4},
{3, 5, 4, 5, 4, 5, 4, 3},
{5, 4, 5, 4, 5, 4, 5, 4},
{3, 5, 4, 5, 4, 5, 4, 3},
{5, 4, 5, 4, 5, 4, 5, 4},
{1, 5, 1, 5, 1, 5, 1, -1}} ;

// l r u d ld, ru 左0 右1 上2 下3 左下4 右上5
 
void fillm(){
	int x = 0, y = 0;
	for(int i = 1; i <= 64; i++){		
		m[x][y] = a[i];
		int op = nxt[x][y];
		if(op == -1)
			break;
		x = x+dx[op];
		y = y+dy[op];
	}
}
void m_mul_q(){
	for(int i = 0; i < 8; i++)
		for(int j = 0; j < 8; j++)
			m[i][j] *= q[i][j];
}
void printm(){
	
	for(int i = 0; i < 8; i++){
		for(int j = 0; j < 8; j++)
			cout << m[i][j] << " ";
		cout << endl;
	}		
}
db exchange(int i, int j){
	db res = 0;
	for(int u = 0; u < 8; u++)
		for(int v = 0; v < 8; v++)
			res += 0.25*f(u) * f(v) * m[u][v] * cos(pi/8*(i+0.5)*u) * cos(pi/8*(j+0.5)*v);
	//res /= 4;
	return res;
}
void change(){
	for(int i = 0; i < 8; i++)
		for(int j = 0; j < 8; j++)
			mres[i][j] = exchange(i, j);
}
void addm(){
	for(int i = 0; i < 8; i++){
		for(int j = 0; j < 8; j++){
			db res = mres[i][j] + 128;
			if(res > 255)
				ans[i][j] = 255;
			else if(res < 0)
				ans[i][j] = 0;
			else 
				ans[i][j] = (int)(res+0.5);
		}
	}
}
void printans(){
	for(int i = 0; i < 8; i++){
		for(int j = 0; j < 8; j++)
			cout << ans[i][j] << " ";
		cout << endl;
	}
}
int n, T;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	for(int i = 0; i < 8; i++)
		for(int j = 0; j < 8; j++)
			cin >> q[i][j];
	cin >> n >> T;
	for(int i = 1; i <= n; i++)
		cin >> a[i];
	if(T == 0){
		fillm(); //3
		printm();
	}	
	else if(T == 1){
		fillm(); //3
		m_mul_q(); //4
		printm();
	}	
	else{
		fillm(); //3
		m_mul_q(); //4
		change();//5
		addm();//6
		printans();
		
	}

	return 0;
}

4 聚集方差

题意:

给定一个树,询问每个子树 u u u g ( A u ) = ∑ i min ⁡ j ≠ i ( a i − a j ) 2 g(A_u) = \sum\limits_i \min\limits_{j \neq i}(a_i-a_j)^2 g(Au)=ij=imin(aiaj)2 A u A_u Au u u u 子树的节点集, i , j i,j i,j 是节点集中的节点, a a a 是节点的点权。

解析:

65分解析:

对于一个集合 A u A_u Au,差的最小绝对值一定是排序后的相邻位置,所以先排序,然后计算 g ( A u ) g(A_u) g(Au),计算一次的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

dfs的时候,合并儿子的子树,然后计算当前节点的 g ( A u ) g(A_u) g(Au)。总的时间复杂度为 O ( n 2 l o g n ) O(n^2logn) O(n2logn)

100解析:树上启发式合并(DSU ON TREE)暂时还不会

65分代码
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 3e5+10;
const ll INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int, int> pii;

int n;
ll fans[maxn];
ll a[maxn];
struct edge{
	int to, nxt;
}e[maxn << 1];
int head[maxn], tot;
void add(int a, int b){
	e[++tot].nxt = head[a];
	e[tot].to = b;
	head[a] = tot;
}
vector<ll> merge(vector<ll> &src1,vector<ll> &src2){
    vector<ll> res;
    int i = 0, j = 0;
    for(; i < src1.size()&& j < src2.size(); ){
        if(src1[i] < src2[j]) 
			res.push_back(src1[i++]);
        else 
			res.push_back(src2[j++]);
    }
    while(i < src1.size()) 
		res.push_back(src1[i++]);
    while(j < src2.size()) 
		res.push_back(src2[j++]);
    return res;
}

ll cal(vector<ll> s){
	if(s.size() == 1)
		return 0;
	sort(s.begin(), s.end());
	ll answ = 0;
	for(int i = 0; i < s.size(); i++){
		ll res = INF;
		if(i > 0)
			res = min(res, (s[i]-s[i-1]) * (s[i]-s[i-1]));
		if(i + 1 < s.size())
			res = min(res, (s[i]-s[i+1]) * (s[i]-s[i+1]));
		answ += res;
	}
	return answ;
}
vector<ll> dfs(int u){
	vector<ll> res;
	res.push_back(a[u]);
	vector<ll> s;
	for(int i = head[u]; i; i = e[i].nxt){
		int v = e[i].to;
		s = dfs(v);
		res = merge(res, s);
	}
	fans[u] = cal(res);
	return res;
}


int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n;
	for(int i = 2; i <= n; i++){
		int x; cin >> x;
		add(x, i);
	}
	for(int i = 1; i <= n; i++)
		cin >> a[i];
	dfs(1);
	for(int i = 1; i <= n; i++)
		cout << fans[i] << endl;
	return 0;
}



202209

1 如此编码

题意:

给定序列 a a a a a a 的前缀积为序列 c c c。给定 m m m m = ∑ i = 1 n c i − 1 × b i m = \sum\limits_{i=1}\limits^nc_{i-1}\times b_i m=i=1nci1×bi。求序列 b b b

解析:

通过取模得到 c i − 1 × b i c_{i-1}\times b_i ci1×bi 的前缀和,然后得到每一个 c i − 1 × b i c_{i-1} \times b_i ci1×bi,然后得到 b i b_i bi

代码:
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define endl '\n'
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int n, m;
int a[maxn], b[maxn], c[maxn]; 
void solve(){
	cin >> n >> m;
	for(int i = 1; i <= n; i++)
		cin >> a[i];
	c[0] = 1;
	for(int i = 1; i <= n; i++)
		c[i] = c[i-1] * a[i];
	for(int i = 1; i <= n; i++)
		b[i] = m % c[i];
	for(int i = n; i >= 2; i--)
		b[i] = b[i]-b[i-1];	
	for(int i = 1; i <= n; i++)
		b[i] = b[i]/c[i-1];
	for(int i = 1; i <= n; i++)	
		cout << b[i] << " ";
	cout << endl;
	return;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	int T = 1;
	while(T--)
		solve();
	return 0;
}


2 何以包邮?

题意:

选一些书,使价值和 m m m 满足 m ≥ x m \ge x mx 且最小

代码:

01背包。

f i , j f_{i,j} fi,j 为前 i i i 本书,价值和为 j j j 是否可行。

状态转移方程为 f i , j = f i − 1 , j ∣ f i − 1 , j − a i f_{i,j} = f_{i-1,j} | f_{i-1,j-a_i} fi,j=fi1,jfi1,jai

初值为 f 0 , 0 = 1 f_{0,0} = 1 f0,0=1

代码:
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 3e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int f[maxn], a[maxn];

int n, x, v;
void solve(){
	cin >> n >> x;
	f[0] = 1;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
		v += a[i];
	}
	
	for(int i = 1; i <= n; i++){
		for(int j = v; j >= a[i]; j--){
			f[j] |= f[j-a[i]];
		}
	}
	
	for(int i = x; i <= v; i++){
		if(f[i]){
			cout << i << endl;
			return;
		}
	}
	return;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	int T = 1;
	while(T--)
		solve();
	return 0;
}


3 防疫大数据

题意:

大模拟

解析:

用set维护当天风险地区,vector保存当前的记录。

代码:
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 1e4+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

struct pmsg{
	int d, u, r;
	pmsg(int d, int u, int r) : d(d), u(u), r(r){}
	pmsg(){}
	
};
vector<pmsg> pm[maxn];
set<int> rm[maxn];
vector<int> res[maxn];

int n, r, m;
void solve(){
	cin >> n;
	for(int i = 0; i < n; i++){
		cin >> r >> m;
		for(int j = 1; j <= r; j++){
			int a;
			cin >> a;
			for(int k = i; k <= min(i+6, n); k++)
				rm[i].insert(a);
		}
		for(int j = 1; j <= m; j++){
			int a, b, c;
			cin >> a >> b >> c;
			pm[i].push_back(pmsg(a, b, c));
		}
		int st = max(i-6, 0);
		for(int j = st; j <= i; j++){
			for(int k = 0; k < pm[j].size(); k++){
				int u = pm[j][k].u;
				int d = pm[j][k].d;
				int r = pm[j][k].r;
				if(d < st)
					continue;
				int flag = 1;	
				for(int kk = d; kk <= i; kk++){
					if(rm[kk].count(r) == 0){
						flag = 0;
						break;
					}
				}
				if(flag)
					res[i].push_back(u);
			}
		}
				
	}
	
	for(int i = 0; i < n; i++){
		cout << i << ": ";
		sort(res[i].begin(), res[i].end());
		res[i].erase(unique(res[i].begin(), res[i].end()), res[i].end());
		
		for(int j = 0; j < res[i].size(); j++)
			cout << res[i][j] << " ";
		cout << endl;
	}

	
	return;
}
int main(){	
	solve();
	return 0;
}

202206

1 归一化处理

代码:
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

db a[maxn], n;
db ave, d;
db f(db x){
	db res = (x-ave)/sqrt(d);
	return res;
}
void solve(){
	cin >> n;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
		ave += a[i];
	}
	ave /= n;
	for(int i = 1; i <= n; i++)
		d += (a[i] - ave) * (a[i] - ave);
	d /= n;	
	for(int i = 1; i <= n; i++)
		cout  << setprecision(12) << f(a[i]) << endl;	
	return;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	int T = 1;
	while(T--)
		solve();
	return 0;
}

2 寻宝!大冒险!

解析:

对每棵树,维护以该树为左下角的附近的树的位置。

代码:
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 1e3+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int n, l, s;
int a[55][55];

struct tree{
	int x, y;
	int c[55][55];
	tree(){memset(c, 0, sizeof(c));}
	tree(int x, int y) : x(x), y(y){c[0][0] = 1;}
	void add(int px, int py){
		if(px < x || px > x + s || py < y || py > y + s)
			return;
		int tx = px - x;
		int ty = py - y;
		c[tx][ty] = 1;
	}
	bool check(){
		if(x > l-s || y > l-s)
			return false;
		for(int i = 0; i <= s; i++){
			for(int j = 0; j <= s; j++){
				if(a[i][j] != c[i][j])
					return false;
			}
		}
		return true;
	}
}t[maxn];


void solve(){
	cin >> n >> l >> s;
	for(int i = 1; i <= n; i++){
		int x, y;
		cin >> x >> y;
		t[i] = tree(x, y);
	}
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= n; j++){
			if(i == j)
				continue;
			int tx = t[j].x;
			int ty = t[j].y;
			t[i].add(tx, ty);
		}
	}
	for(int i = s; i >= 0; i--){
		for(int j = 0; j <= s; j++){
			cin >> a[i][j];
		}
	}
	int ans = 0;
	for(int i = 1; i <= n; i++){
		if(t[i].check())
			ans++;
	}
	cout << ans << endl;
	return;
	
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	int T = 1;
	while(T--)
		solve();
	return 0;
}

3 角色授权

题意:

大模拟

解析:

每个角色是三个集合,存该角色的信息。

用map存名字字符串到类的映射,从而根据名快速找到类。

注意常数级别的优化。

代码:
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
#define endl '\n'
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;
typedef set<string>::iterator S_IT;

struct role{
	set<string> op;
	set<string> kind;
	set<string> source;
};
struct group{
	set<string> ro;
};
struct people{
	set<string> gr;
	set<string> ro;
};

map<string, role> mprole;
map<string, group> mpgroup;
map<string, people> mppeople;

bool check(people user, string opt, string kindt, string sourcet){
	for(S_IT it = user.ro.begin(); it != user.ro.end(); it++){
		string roname = *it;
		int flag1 = mprole[roname].op.count("*") || mprole[roname].op.count(opt);
		if(flag1 == 0) continue;
		int flag2 = mprole[roname].kind.count("*") || mprole[roname].kind.count(kindt);			
		if(flag2 == 0) continue;
		int flag3 = mprole[roname].source.empty() || mprole[roname].source.count(sourcet);
		if(flag3 == 0) continue;
		return true;
	}
	for(S_IT it = user.gr.begin(); it != user.gr.end(); it++){
		string grname = *it; 
		if(mpgroup.count(grname) == 0)
			continue;
		for(S_IT itt = mpgroup[grname].ro.begin(); itt != mpgroup[grname].ro.end(); itt++){
			string roname = *itt;
			int flag1 = mprole[roname].op.count("*") || mprole[roname].op.count(opt);
			if(flag1 == 0) continue;
			int flag2 = mprole[roname].kind.count("*") || mprole[roname].kind.count(kindt);			
			if(flag2 == 0) continue;
			int flag3 = mprole[roname].source.empty() || mprole[roname].source.count(sourcet);
			if(flag3 == 0) continue;
			return true;
		}
	}
	return false;
}
int n, m, q;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n >> m >> q;
	string name, tmp;
	int nv, no, nn;
	for(int i = 1; i <= n; i++){ //角色role 
		cin >> name  >> nv;
		for(int j = 1; j <= nv; j++){
			cin >> tmp;
			mprole[name].op.insert(tmp);
		}
		cin >> no;
		for(int j = 1; j <= no; j++){
			cin >> tmp;
			mprole[name].kind.insert(tmp);
		}
		cin >> nn;
		for(int j = 1; j <= nn; j++){
			cin >> tmp;
			mprole[name].source.insert(tmp);
		}
	}
	int ns; 
	string ch, namet;
	for(int i = 1; i <= m; i++){ //角色关联 
		cin >> name >> ns;
		for(int j = 1; j <= ns; j++){
			cin >> ch >> namet;
			if(ch == "g")
				mpgroup[namet].ro.insert(name);
			else
				mppeople[namet].ro.insert(name);
		}
	}
	
	int ng;
	string opt, kindt, sourcet;
	for(int i = 1; i <= q; i++){ //行为 
		cin >> name >> ng;
		for(int i = 1; i <= ng; i++){
			cin >> namet;
			mppeople[name].gr.insert(namet);
		}
		cin >> opt >> kindt >> sourcet;
		if(check(mppeople[name], opt, kindt, sourcet))
			cout << 1 << endl;
		else
			cout << 0 << endl;
		mppeople[name].gr.clear();
	} 
	
	return 0;
}

5 PS无限版

题意:

实现点的平移,旋转,放缩,对称,投影

询问一群点的重心、到某点距离的平方和

解析:

关于投影操作,过定作垂线,交点即为投影点

过于对称操作,先求出投影点,然后平移即可

代码:

暂时有锅

202203

1 未初始化警告

代码:
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int n, k;
int vis[maxn];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	cin >> n >> k;
	int cnt = 0;
	vis[0] = 1;
	for(int i = 1; i <= k; i++){
		int x, y;
		cin >> x >> y;
		if(!vis[y])
			cnt++;
		vis[x] = 1;
		
	}
	cout << cnt << endl;
	return 0;
}


2 出行计划

解析:

区间修,单点查询。

差分即可(线段树、树状数组也行)

代码:
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 1e6+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

int lowbit(int x){return x & (-x);}
int cc[maxn];
int maxx;
void insert(int x, int v){
	while(x <= maxx){
		cc[x] += v;
		x += lowbit(x);
	}
}
void update(int l, int r, int v){
	insert(l, v);
	insert(r+1, -v);
}
int query(int x){
	int res = 0;
	while(x){
		res += cc[x];
		x -= lowbit(x);
	}
	return res;
}


int t[maxn], c[maxn];
int n, m, k, maxt;
//[t-k-c+1, t-k]
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n >> m >> k;
	for(int i = 1; i <= n; i++){
		cin >> t[i] >> c[i];
		maxt = max(maxt, t[i]+c[i]); 
	}
	maxx = maxt;
	for(int i = 1; i <= n; i++){
		if(t[i]-k < 1)
			continue;
		update(max(1, t[i]-k-c[i]+1), t[i]-k, 1);
	}
	for(int i = 1, q; i <= m; i++){
		cin >> q;
		int res = query(q);
		//cout << "ans = ";
		cout << res << endl;
	}
	return 0;
}



3 计算资源调度器

题意:

大模拟

代码:
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;
struct node{
	int id;
	int pos;
	int cnt;
	set<int> task;
	node(){}
	node(int id, int pos, int cnt) : id(id), pos(pos), cnt(cnt){}
	bool operator < (const node &b) const{
		if(cnt == b.cnt)
			return id < b.id;
		else
			return cnt < b.cnt;
	}
};
unordered_map<int, node> mp;
typedef unordered_map<int, node>::iterator M_IT;
typedef vector<int>::iterator V_IT;
vector<int> id;
int n, m;
void require_na(int x, vector<int> &b){ //节点亲和性
	for(auto it = b.begin(); it != b.end();){
		if(mp[*it].pos != x)
			b.erase(it);
		else
			it++;
	}
} 
void require_pa(int x, vector<int> &b){ //任务亲和性 
	set<int> s;//区 
	for(auto it = mp.begin(); it != mp.end(); it++){
		if((*it).se.task.count(x))
			s.insert((*it).se.pos);
	} 
	for(auto it = b.begin(); it != b.end();){
		if(s.count(mp[*it].pos) == 0)
			b.erase(it);
		else
			it++;
	}
}
vector<int> require_paa(int x, vector<int> &b){
	vector<int> res;
	for(int i = 0; i < b.size(); i++)
		res.push_back(b[i]); //copy
		
	for(auto it = res.begin(); it != res.end();){
		if(mp[*it].task.count(x))
			res.erase(it);
		else
			it++;
	}
	return res;
}
bool cmp(int a, int b){
	return mp[a] < mp[b];
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> n >> m;
	for(int i = 1; i <= n; i++){
		int pos; cin >> pos;
		mp[i] = node(i, pos, 0);
		id.push_back(i);
	}
	int T; cin >> T;
	while(T--){
		int q, a, na, pa, paa, paar;
		cin >> q >> a >> na >> pa >> paa >> paar;

		while(q--){
			vector<int> tmp = id;
			vector<int> tmp2;
			vector<int> res;
			if(na) //节点亲和性 
				require_na(na, tmp);
			if(pa) //任务亲和性 
				require_pa(pa, tmp);
			if(paa) //反亲和性 
				tmp2 = require_paa(paa, tmp);	
				
									
			if(!paa) // 没有反亲和 
				res = tmp;
			else{    //有反亲和 
				if(tmp2.size() == 0 && paar == 0)  // 尽量满足且结果空
					res = tmp;
				else
					res = tmp2; 
			}	
			if(res.size() == 0){
				cout << 0 << " ";
				continue;
			}			
			sort(res.begin(), res.end(), cmp);
			int nodeid = res[0];	
			cout << nodeid << " ";
			mp[nodeid].cnt++;
			mp[nodeid].task.insert(a); 
		}
		cout << endl;
	}
	return 0;
}


202112

1 序列查询

解析:

分段求和

代码:
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

ll n, N;
ll a[maxn];
ll ans;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	
	cin >> n >> N;
	for(int i = 1; i <= n; i++)
		cin >> a[i];
	a[n+1] = N;
	for(int i = 0; i <= n; i++){
		ll cnt = a[i+1]-a[i];
		ans += cnt * i;;
	}
	cout << ans << endl;
	return 0;
}


2 序列查询新解

解析:

f i f_i fi 分段,每一段内 f f f 值不变, g g g 单调不减。
如果 g m a x ≤ f g_{max} \le f gmaxf g m i n ≥ f g_{min} \ge f gminf 直接求和即可;否则分为两段分别求和。

g g g 求和的时候,可以先计算前缀和

代码:
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> pii;

ll n, N;
ll a[maxn];
ll ans, r;
ll g(int x){
	return x/r;
}

ll sumg(int x){
	ll cnt = (x+1)/r;
	ll res = (cnt)*(cnt-1)/2*r + (x+1)%r*cnt;
	return res;
}
ll cal(ll ql, ll qr, ll f){
	return abs(sumg(qr)-sumg(ql-1) - (qr-ql+1) * f);
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	


	cin >> n >> N;
	r = N/(n+1);
	for(int i = 1; i <= n; i++)
		cin >> a[i];
	a[n+1] = N;
	for(int i = 0; i <= n; i++){
		ll ql = a[i];
		ll qr = a[i+1]-1;
		ll f = i;
		
		if(g(qr) <= f || g(ql) >= f)
			ans += cal(ql, qr, f);		
		else{
			int pos = r * f;
			ans += cal(ql, pos, f);
			ans += cal(pos+1, qr, f);
		}
	}
	cout << ans << endl;
	return 0;
}

3 登机牌条码

题意:

大模拟

解析:

对编码数据产生数字,然后用数字产生码字。

对于校验码: x k d ( x ) ≡ q ( x ) g ( x ) − r ( x ) x^kd(x)\equiv q(x)g(x)-r(x) xkd(x)q(x)g(x)r(x) g ( x ) g(x) g(x) 已知
所以 x k d ( x ) ≡ − r ( x )   ( m o d    g ( x ) ) x^kd(x)\equiv -r(x) \:(mod\;g(x)) xkd(x)r(x)(modg(x))

k k k 次多项式 g k ( k ) = g k − 1 ( x ) × ( x − 3 k ) g_k(k) = g_{k-1}(x) \times (x-3^k) gk(k)=gk1(x)×(x3k)

多项式除法模拟即可

代码:
#include
using namespace std;
typedef long long ll;
typedef double db;
#define fi first
#define se second
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
const int mod = 929;
typedef pair<int, int> pii;

int curmode;
vector<int> number;
vector<int> code;
vector<int> verify;
vector<int> gx;
vector<int> d;
int g[2][maxn];
int w, s, vlen;
string str;

ll qpow_mod(ll a, ll b){
	ll res = 1;
	while(b){
		if(b&1)
			res = res * a % mod;
		a = a * a % mod;
		b = b >> 1;
	}
	return res;
}
void putnumber(char c){
	if(c >= '0' && c <= '9'){
		if(curmode != 2){
			number.push_back(28);			
			curmode = 2;
		}
		number.push_back(c-'0');
	}
	else if(c >= 'a' && c <= 'z'){
		if(curmode != 1){
			number.push_back(27);
			curmode = 1;
		}
		number.push_back(c-'a'); 
	}
	else if(c >= 'A' && c <= 'Z'){
		if(curmode == 1){
			number.push_back(28);
			number.push_back(28);
		}
		if(curmode == 2)
			number.push_back(28);
		
		curmode = 0;
		number.push_back(c-'A');
	}
} 
void get_gx(int k){
	memset(g, 0, sizeof(g));
	int cur = 1, pre = 0;
	
	g[pre][0] = -3;
	g[pre][1] = 1; 
	for(int i = 2; i <= k; i++){
		// (x-3^i)
		g[cur][0] = 0;
		ll c = (mod - qpow_mod(3, i)) % mod;
		for(int j = 1; j <= k+1; j++)
			g[cur][j] = g[pre][j-1];
		for(int j = 0; j <= k+1; j++){
			ll tmp = (g[pre][j] * c) % mod;
			g[cur][j] = ((g[cur][j] + tmp) % mod + mod) % mod;
		}
		
		cur ^= 1;
		pre ^= 1;
	}
	
	for(int i = 0; i <= k; i++)
		gx.push_back(g[pre][i]);
}
void print(vector<int> &b){
	for(auto x : b)
		cout << x << " ";
	cout << endl;
}
void get_verify(int k){
	for(int i = 1; i <= k; i++)
		d.push_back(0);
	for(int i = code.size()-1; i >= 0; i--) //倒着 
		d.push_back(code[i]); 
	//print(gx);	
	//print(d);
	for(int i = d.size()-1; i >= k; i--){
		int cnt = d[i];
		for(int j = 0; j < gx.size(); j++){
			d[i-j] = d[i-j] - cnt * gx[gx.size()-1-j];
			d[i-j] = (d[i-j]%mod+mod)%mod;
		}
	} 
	for(int i = k-1; i >= 0; i--){
		int x = (mod-d[i])%mod;
		cout << x << endl;
	}
}
int main(){	
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	
	cin >> w >> s;
	cin >> str;	
		
	for(int i = 0; i < str.length(); i++)
		putnumber(str[i]);
	if(number.size()%2)
		number.push_back(29);
	
	code.push_back(0);
	for(int i = 0; i < number.size(); i += 2){
		int res = 30 * number[i] + number[i+1];
		code.push_back(res);
	}
	
	if(s == -1){
		while(code.size()%w != 0)
			code.push_back(900);
		code[0] = code.size();	
		for(int i = 0; i < code.size(); i++)
			cout << code[i] << endl;
	}
	else{
		vlen = qpow_mod(2, s+1);
		while((vlen+code.size())%w != 0)
			code.push_back(900);
		code[0] = code.size();
		for(int i = 0; i < code.size(); i++)
			cout << code[i] << endl;
			
		get_gx(vlen);
		get_verify(vlen);
	}
	
	return 0;
}

你可能感兴趣的:(图论,算法)