第二十一届西南科技大学同步赛
第二十一届西南科技大学同步赛
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\) 的连续子串都不会重复
如果在取字符的过程中,优先队列为空,那么就不存在满足题意的构造

浙公网安备 33010602011771号