第二十一届西南科技大学同步赛

第二十一届西南科技大学同步赛

https://ac.nowcoder.com/acm/contest/109520/A

A:
题目大意:

map<char,char> mp;

void solve(){
	int n,m;
	cin>>n>>m;
	string s;
	cin>>s;
	for (int i='a';i<='z';i++) mp[i]=i;
	while (m--){
		char u,v;
		cin>>u>>v;
		for (auto &it:mp){
			if (it.second==u) it.second=v;
			else if (it.second==v) it.second=u;
		}
	}
	for (auto it:s) cout<<mp[it];
}

map 映射每个字母实际对应的值

B:

题目大意:

void solve(){
	int a,b;
	cin>>a>>b;
	if (a<b) swap(a,b);
	if (a%b){
		cout<<-1<<endl;
		return;
	}
	if (a==b){
		cout<<1<<endl;
		cout<<a<<endl;
		return;
	}
	cout<<2<<endl;
	cout<<a-b<<' '<<b<<endl;
}

构造题,因为需要满足 \(gcd(x_1,x_2,\cdots,x_k)=min(a,b)\) ,假定 \(a>b\) ,那么所有的 \(x_i\) 都应该为 \(b\) 的整倍数

设任意的 \(x_i=\alpha_i*b\),则 \(a=\sum_{i=1}^k x_i=b*\sum_{i=1}^k \alpha_i\)所以 \(a\) 也为 \(b\) 的整倍数,之后当 \(a\ne b\) 时构造 \((a-b,b)\) 即可

C:
题目大意:

void solve(){
	int a,b;
	cin>>a>>b;
	if ((a+b)%2||min(a,b)==0&&max(a,b)==2){
		cout<<"no"<<endl;
	}else
		cout<<"yes"<<endl;
}

首先可以明确一点,当两端水总和为奇数时一定不能平衡

然后根据唯一分解定理,任意一个大于 \(1\) 的数都能被表示为若干个质因子的乘积,设 \(x=\lvert a-(a+b)/2\rvert\) 表示能使天平平衡的需要转移的水的质量,\(x>1\) 时,显然有解、当 \(x=1\)\((a,b)=(0,2)\) 时,无解

D:
题目大意:

void solve(){
	int n;
	cin>>n;
	vector<pair<int,int>> a(n+1);
	for (int i=1;i<=n;i++) cin>>a[i].second;
	for (int i=1;i<=n;i++) cin>>a[i].first;
	sort(a.begin()+1,a.end(),[](pair<int,int> x,pair<int,int> y){return x.second>y.second;});
	LL sum=0;
	for (int i=1;i<=n;i++)
		sum+=1ll*(a[i].first-i+1)*a[i].second;
	cout<<sum<<endl;
}

贪心做法,先吃甜度高的,甜度高的放在后面吃对答案的负贡献越大

设甜度 \(a_i>a_j\),如果都放在第 \(x\) 个吃,那么相比第 \(x-1\) 个吃对答案的贡献为 \(-a_i<-a_j\)所以把甜度高的放在后面吃一定劣

I:
题目大意:

void solve(){
	string s[4];
	for (int i=1;i<=3;i++) cin>>s[i];
	for (int i=0;i<s[1].size();i++){
		int st[30]={0};
		for (int j=1;j<=3;j++) st[s[j][i]-'0']++;
		for (int j=0;j<26;j++){
			if (st[j]>=2) cout<<(char)(j+'0');
		}
	}
}

签到题,如果一位密码 \(c\) 有两个人以上相同,那么这一位密码就是 \(c\)

M:

题目大意:

struct edge{
	LL v,w,g,r;
};

struct node{
	LL d,now,cnt,tim;
	bool operator <(const node &x) const{
		return d>x.d;
	}
};

vector<edge> e[50010];
int n,m,st,ed;
LL dis[50010][3];
bool vis[50010][3];

void dijkstra(){
	priority_queue<node> q;
	for (int i=0;i<=n;i++)
		for (int j=0;j<=2;j++)
			dis[i][j]=LLinf;
	dis[st][0]=0;
	q.push({0,st,0,0});
	while (q.size()){
		auto t=q.top();
		q.pop();
		if (vis[t.now][t.cnt]) continue;
		vis[t.now][t.cnt]=1;
		for (auto it:e[t.now]){
			if (t.tim%(it.g+it.r)<=(it.g-it.w)){
				if (dis[it.v][t.cnt]>dis[t.now][t.cnt]+it.w){
					dis[it.v][t.cnt]=dis[t.now][t.cnt]+it.w;
					q.push({dis[it.v][t.cnt],it.v,t.cnt,t.tim+it.w});
				}
			}else{
				if (t.cnt<2){
					if (dis[it.v][t.cnt+1]>dis[t.now][t.cnt]+it.w){
						dis[it.v][t.cnt+1]=dis[t.now][t.cnt]+it.w;
						q.push({dis[it.v][t.cnt+1],it.v,t.cnt+1,t.tim+it.w});
					}
				}
				if (dis[it.v][t.cnt]>dis[t.now][t.cnt]+it.w+(it.g+it.r-t.tim%(it.g+it.r))){
					dis[it.v][t.cnt]=dis[t.now][t.cnt]+it.w+(it.g+it.r-t.tim%(it.g+it.r));
					q.push({dis[it.v][t.cnt],it.v,t.cnt,t.tim+it.w+(it.g+it.r-t.tim%(it.g+it.r))});
				}
			}
		}
	}
}

void solve(){
	cin>>n>>m>>st>>ed;
	for (int i=1;i<=m;i++){
		LL u,v,w,g,r;
		cin>>u>>v>>w>>g>>r;
		e[u].push_back({v,w,g,r});
	}
	dijkstra();
	LL ans=min(dis[ed][0],min(dis[ed][1],dis[ed][2]));
	if (ans>=1e18) cout<<-1;
	else cout<<ans;
}

分层图最短路 \(dis_{i,j}\) 表示走到第 \(i\) 个节点用 \(j\) 次技能的最短时间

更新路径时,如果当前时间可以通过这个节点 \(v\) ,那么不使用技能之间通过并更新队列

如果不能通过这个节点 \(v\) ,则考虑使用技能强行通过或者等待一个周期通过

if (t.cnt<2){
	if (dis[it.v][t.cnt+1]>dis[t.now][t.cnt]+it.w){
		dis[it.v][t.cnt+1]=dis[t.now][t.cnt]+it.w;
		q.push({dis[it.v][t.cnt+1],it.v,t.cnt+1,t.tim+it.w});
	}
}//使用技能强行通过

if (dis[it.v][t.cnt]>dis[t.now][t.cnt]+it.w+(it.g+it.r-t.tim%(it.g+it.r))){
	dis[it.v][t.cnt]=dis[t.now][t.cnt]+it.w+(it.g+it.r-t.tim%(it.g+it.r));
	q.push({dis[it.v][t.cnt],it.v,t.cnt,t.tim+it.w+(it.g+it.r-t.tim%(it.g+it.r))});
}//等待一轮周期,需要等待的时间为(it.g+it.r-t.tim%(it.g+it.r)

F:

题目大意:

int n,m,k;
bool pep[30][30];
int w[30];
bool vis[30];
unordered_map<int,int> mp;
LL ans;

void dfs(int x,int cnt){

	if (cnt>=k){
		LL sum=0;
		for (auto it:mp)
			if (it.second>=2)
				sum+=1ll*w[it.first]*it.second;
		ans=max(ans,sum);
		return;
	}
	
	for (int i=x+1;i<=m;i++){
		for (int j=1;j<=26;j++)
			if (pep[i][j]) mp[j]++;
		dfs(i,cnt+1);
		for (int j=1;j<=26;j++)
			if (pep[i][j]) mp[j]--;
	}
}

void solve(){
	cin>>n>>m>>k;
	for (int i=1;i<=n;i++) cin>>w[i];
	for (int i=1;i<=m;i++){
		int cnt;
		cin>>cnt;
		for (int j=1;j<=cnt;j++){
			char c;
			cin>>c;
			pep[i][c-'A'+1]=1;
		}
	}
	dfs(0,0);
	cout<<ans;
}

DFS 选出来的人的组合,过程中用 map 记录每种属性的个数,要开 long long 防止越界

时间复杂度为 \(O(\dbinom{m}{k})\) ,在给定的数据范围内的最坏情况为 \(O(2e6)\) 没有超时问题

K:

题目大意:

const int N=1e5+10;

struct node{
	int l,r,sum;
};

node tr1[4*N],tr2[4*N];
int a[N]; 
int n,q,id,k;
int x,y;

void build1(int l,int r,int p){
	tr1[p]={l,r,a[l]};
	if (l==r) return ;
	int mid=l+r>>1;
	build1(l,mid,Lc);
	build1(mid+1,r,Rc);
	tr1[p].sum=tr1[Lc].sum&tr1[Rc].sum;
}

void build2(int l,int r,int p){
	tr2[p]={l,r,a[l]};
	if (l==r) return ;
	int mid=l+r>>1;
	build2(l,mid,Lc);
	build2(mid+1,r,Rc);
	tr2[p].sum=tr2[Lc].sum|tr2[Rc].sum;
}

int query1(int x,int y,int p){
	if (x<=tr1[p].l&&y>=tr1[p].r)
		return tr1[p].sum;
	int mid=tr1[p].l+tr1[p].r>>1;
	int res=(1<<31)-1;
	if (x<=mid) res&=query1(x,y,Lc);
	if (y>mid) res&=query1(x,y,Rc);
	return res;
}

int query2(int x,int y,int p){
	if (x<=tr2[p].l&&y>=tr2[p].r)
		return tr2[p].sum;
	int mid=tr2[p].l+tr2[p].r>>1;
	int res=0;
	if (x<=mid) res|=query2(x,y,Lc);
	if (y>mid) res|=query2(x,y,Rc);
	return res;
}

bool judge1(int mid){
	return query1(id-mid,id,1)>=x;
}

bool judge2(int mid){
	return query2(id,id+mid,1)<=y;
}

void solve(){
	
	cin>>n>>q;
	for (int i=1;i<=n;i++) cin>>a[i];
	build1(1,n,1);
	build2(1,n,1);
	
	while(q--){
		cin>>id>>k;
		x=k-a[id],y=k+a[id];
		
		if (x>a[id]||y<a[id]) {
            cout<<-1<<endl;
            continue;
        }
		
		int l=-1,r=id;
		while (l+1!=r){
			int mid=l+r>>1;
			if (judge1(mid))
				l=mid;
			else
				r=mid;
		}
		cout<<id-l<<' ';
		
		l=-1,r=n-id+1;
		while (l+1!=r){
			int mid=l+r>>1;
			if (judge2(mid))
				l=mid;
			else
				r=mid;
		}
		cout<<id+l<<endl;
	}
}

int main()
{
	cintie;
	Trd;
	
	
	return 0;
}

不难发现对连续区间 \([l,r]\) 中元素进行 \(and,or\) 操作的单调性,所以可以通过两次二分计算左右区间延伸的长度

标程是通过拆位然后利用前缀和维护区间 \(and,or\) ,用线段树 \(O(\log n)\) 查询也可以卡过去( \(850ms\)

**总时间复杂度都是一样的 \(O(n+q\log n)\) **

L:
题目大意:

void solve(){
	string s;
	int k;
	cin>>s>>k;
	unordered_map<char,int> mp;
	for (auto c:s)	mp[c]++;
	
	if (k==1||k>s.size()){
		cout<<s<<endl;
		return;
	}
	
	priority_queue<pair<int,char>> q;
	queue<pair<int,char>> qq;
	
	for (char i='a';i<='z';i++){
		if (mp[i])
			q.push({mp[i],i});
	}
	
	string ans;
	while(true){
		for (int i=1;i<=k;i++){
            
			if (q.empty()){
				cout<<-1<<endl;
				return;
			}
            
			auto t=q.top();
			q.pop();
			ans+=t.second;
            
			if (ans.size()==s.size()){
				cout<<ans<<endl;
				return;
			}
            
			if (t.first-1)
				qq.push({t.first-1,t.second});
		}
		while (qq.size()){
			auto t=qq.front();
			q.push(t);
			qq.pop();
		}
	}
}

int main()
{
	cintie;
	Trd;
	
	
	return 0;
}

贪心构造,考虑特殊情况,\(k=1\) 或者 \(k\) 大于 \(s\) 的长度时,显然无解

这里定义子串为一段长 \(k\) 的连续串,为了保证这个子串不存在相同的字符,那么对每段长为 \(k\) 的子串都构造不同的字符进去

用优先队列维护可选元素的集合,按照字符个数从大到小排列

每次选取个数最多的字符加入答案,一旦选取后将这个字符从优先队列中移除,然后放进普通队列里面准备在下一段 \(k\) 子串中重新加入优先队列。这样保证了任意一段长为 \(k\) 的连续子串都不会重复

如果在取字符的过程中,优先队列为空,那么就不存在满足题意的构造

posted @ 2025-05-12 19:55  才瓯  阅读(30)  评论(0)    收藏  举报