上海11月月赛乙组解题报告

1.数对统计

题目描述
给定 n 个数字 a1,a2,……,an,请从中挑选两个数字,并按原顺序组成一个数对。请问能选出多少种不完全相等的数对?

输入格式
第一行,单个整数 n
第二行,n 个整数 a1,a2,……,an

输出格式
单个整数:表示不相等的数对数量。

数据范围
对于 30% 的数据,n≤10
对于 60% 的数据,n≤1000
对于 100% 的数据,1≤n≤100000
1≤ai≤n
样例数据
输入:
4
3 1 3 2
输出:
5
说明:
(3,1)
(3,3)
(3,2)
(1,3)
(1,2)

对于数组中的每一个数,如果有多个相同的数x,那么后面的数所组成的数对一定与第一个x所组成的数对重复。而对于数列中的相同的数y,也只能有一个作为数对中的y。
可以记录所有第一个x的出现位置,并从后往前统计不同的y的个数。

#include <bits/stdc++.h>
using namespace std;int n,a[100010],pos[100010];
bool vh[100010];int main(){cin >> n ;for(int i=1;i<=n;++i){cin >> a[i];if(pos[a[i]]==0)pos[a[i]]=i;}long long p=0,ans=0;for(int i=n;i>=1;--i){if(i==pos[a[i]])ans+=p;if(vh[a[i]]==0){p++;vh[a[i]]=1;}}cout << ans << endl;return 0;
}

2.加与乘

题目描述
给定 n 个整数 a1,a2,……,an,一开始,所有数字都是 0,接下来将根据输入数据依次进行 q 条修改操作:

加法修改操作以字符 + 开头,后接两个整数 p 与 d,表示数列的第 p 项将增加 d;
乘法修改操作以字符 * 开头,后接一个整数 m,表示数列的每一项都将乘以 m。
请输出经过修改后数列,由于答案可能很大,输出每一个数字模 1,000,000,007 的余数。

输入格式
第一行:两个整数表示 n 与 q。
第二行到第 q+1 行:第 i+1 行首先有一个字符表示操作类型,若是加法修改,后接两个整数 pi与 di ,若是乘法修改,后接一个整数 mi。

输出格式
单独一行:n 个数字表示修改后每个数字模 1,000,000,007 的余数。

数据范围
对于 40% 的数据,n,q≤1000
对于 80% 的数据,n,q≤50000
对于 100% 的数据,n,q≤200,000
1≤di ,mi≤1,000,000
样例数据
输入:
3 5
+1 3
*10
+2 6
+3 9
*5
输出:
150 30 45

因为有点更新和区间更新,所以打了一个线段树的程序

每次乘法就将树的根节点做一个懒标记,而每次加法就将懒标记(如果有的话)下沉,找到该节点时就将该节点的值与懒标记(如果有的话)相乘,再加上要累加的值。查询时同样需要下沉懒标记,输出数组中的值与懒标记相乘的结果。

附上线段树的程序:

#include <bits/stdc++.h>
using namespace std;int n,m,p,q;
long long g=1000000007;
char a;
struct nod{int l;int r;long long lazy;long long sum;
}tree[20000010];void pushdown(int k,long long v){//下沉子节点int lc=2*k,rc=2*k+1;if(tree[lc].lazy!=-1){tree[lc].lazy*=v;tree[lc].lazy%=g;}elsetree[lc].lazy=v;if(tree[rc].lazy!=-1){tree[rc].lazy*=v;tree[rc].lazy%=g;}elsetree[rc].lazy=v;tree[k].lazy=-1;
}void build(int k,int l,int r){//构树tree[k].l=l;tree[k].r=r;tree[k].lazy=-1;if(l==r){tree[k].sum=0;return;}int mid=(l+r)/2,lc=k*2,rc=k*2+1;build(lc,l,mid);build(rc,mid+1,r);tree[k].sum=0;
}void add(int k,int i,long long v){//累加if(tree[k].l==tree[k].r && tree[k].l==i){if(tree[k].lazy==-1){tree[k].sum+=v;tree[k].sum%=g;}else{tree[k].sum*=tree[k].lazy;tree[k].sum%=g;tree[k].sum+=v;tree[k].sum%=g;tree[k].lazy=-1;}return;}if(tree[k].lazy!=-1)pushdown(k,tree[k].lazy);int mid=(tree[k].l+tree[k].r)/2,lc=2*k,rc=2*k+1;if(i<=mid)add(lc,i,v);elseadd(rc,i,v);tree[k].sum=(tree[lc].sum+tree[rc].sum)%g;tree[k].sum%=g;
}void query(int k,int l,int r){//查询if(tree[k].l==tree[k].r){if(tree[k].lazy==-1)cout << tree[k].sum%g << " ";elsecout << (tree[k].sum*tree[k].lazy)%g << " ";return;}if(tree[k].lazy!=-1)pushdown(k,tree[k].lazy);int mid=(tree[k].l+tree[k].r)/2,lc=2*k,rc=2*k+1;query(lc,l,mid);query(rc,mid+1,r);
}int main(){cin >> n >> m;build(1,1,n);for(int i=1;i<=m;++i){cin >> a;if(a=='+'){cin >> p >> q;add(1,p,q);}else{cin >> p;if(tree[1].lazy==-1)tree[1].lazy=p;else{tree[1].lazy*=p;tree[1].lazy%=g;}}}query(1,1,n);return 0;
}

不过这个题目用线段树显然有些大材小用了,还可以预处理出每一次操作i需要乘上的值(偷看解题报告后 )

对于操作i加上的数,后面的乘法都要乘,前面的乘法都不要乘,那么mul[i]=mul[i+1]*mul[i]%p。

#include <bits/stdc++.h>
using namespace std;long long p=1000000007;
int n,q;
long long x,y,mul[200010];
char c;
struct nod{long long data;int op;nod(long long _data,int _op):data(_data),op(_op){}
};
vector<nod> g[200010];int main(){cin >> n >> q;for(int i=0;i<200010;++i)mul[i]=1;for(int i=1;i<=q;++i){cin >> c >> x;if(c=='+'){cin >> y;g[x].push_back(nod(y,i));}elsemul[i]=x;}for(int i=q;i>=1;--i)mul[i]=mul[i+1]*mul[i]%p;for(int i=1;i<=n;++i){long long sum=0;for(int j=0;j<g[i].size();++j){sum+=g[i][j].data*mul[g[i][j].op]%p;sum%=p;}cout << sum << " ";}cout << endl;return 0;
}

3.菜单设计

题目描述
小爱作为餐厅经理,他需要从 n 道备选菜肴中,选出 m 道菜品作为餐厅新一季度的正式菜单。当然,正式菜单中菜品的先后品尝顺序也会影响顾客的感受。
已知第 i 道备选菜肴的美味值为 xi,但有额外 p 条菜品搭配建议,第 i 条建议包含三个参数 ai 、bi和 ci,表示顾客在品尝完第 ai 道菜肴后,下一道直接品尝第 bi道菜肴,会有额外 ci点美味值。

​问作为餐厅经理的小爱如何选择并设计安排菜肴及上菜顺序,才能使顾客品尝后的美味值最大。

输入格式
输入第一行,两个正整数n,m
输入第二行,n个正整数x1,…,xn

输入第三行,一个正整数,p
​接下来 p 行,每行3个正整数,分别表示第ii条建议的三个参数ai,bi,ci

输出格式
输出一个正整数,表示所选菜单能获得的最大美味值。

数据范围
对于 50% 数据,1≤m≤n≤8;
对于 100% 数据,1≤m≤n≤18;
0≤xi,ci≤109 ,1≤ai,bi≤n ,1≤p≤n×(n−1) 。
数据保证没有两条菜品建议的ai和bi完全相同

样例数据
输入:
4 3
2 5 1 3
2
2 1 6
4 3 1
输出:
16
说明:
按第2道、第1道、第4道备选菜肴的顺序组成正式菜单
获得 2+5+3=10 点美味值,外加第2道后接第1道备选菜肴所带来的 6 点额外美味值
故最大美味值为 16

直接打暴力

#include <bits/stdc++.h>
using namespace std;int m,n,p,a[25],x,y,z,ans=0;
int g[25][25],dp[25][25];
bool vh[25];int f(int dep,int v){if(dep==0)return 0;int mv=0;for(int i=1;i<=m;++i){if(vh[i]==1)continue;vh[i]=1;mv=max(mv,f(dep-1,i)+a[i]+g[i][v]);vh[i]=0;}return mv;
}
int main(){memset(dp,-1,sizeof(dp));cin >> m >> n;for(int i=1;i<=m;++i)cin >> a[i];cin >> p;for(int i=1;i<=p;++i){cin >> x >> y >> z;g[x][y]=z;}//dfs(1,0,0);for(int i=1;i<=m;++i){vh[i]=1;ans=max(ans,f(n-1,i)+a[i]);vh[i]=0;}cout << ans << endl;return 0;
}

状压dp

用f(s,u)来表示当前最大值,u指前一道菜选了什么,s是一串二进制数,1表示这道菜肴已经选了,0表示没选,每次从没选的菜肴中挑出一道菜肴递归,当s中1的个数到达m时为边界,加记忆化就可以过了。(这里用bitset来存储一串二进制数)。

#include <bits/stdc++.h>
using namespace std;int n,m,p,de[25],a,b,c,g[25][25];
long long dp[270000][25];
bitset<25> bit;long long f(bitset<25> bit,int u){if(dp[bit.to_ulong()][u]!=-1)return dp[bit.to_ulong()][u];if(bit.count()==m)return dp[bit.to_ulong()][u]=0;long long mv=0;for(int i=1;i<=n;++i){if(bit[i-1]==0){bit.flip(i-1);mv=max(mv,f(bit,i)+de[i]+g[u][i]);bit.flip(i-1);}}return dp[bit.to_ulong()][u]=mv;
}int main(){memset(dp,-1,sizeof(dp));cin >> n >> m;for(int i=1;i<=n;++i)cin >> de[i];cin >> p;for(int i=1;i<=p;++i){cin >> a >> b >> c;g[a][b]=c;}bitset<25> bit;long long ans=0;for(int i=1;i<=n;++i){bit.set(i-1);ans=max(ans,f(bit,i)+de[i]);bit.reset(i-1);}cout << ans << endl;return 0;
}

4.树的链接

题目描述
给定一张 n 个点,若干条带权无向边的图。初始时,没有任何一条边存在。

​你需要按序完成给定的 q 个操作,每个操作为以下两种操作类型之一:

x y 表示询问操作,询问点 x 与 点y 之间的最短路径长度;如果两点之间不存在任何路径,则输出-1。

x y w 表示链接操作,即在点 x 与 点y 之间新建一条长度为 w 的边。

输入格式
输入的第一行包含两个正整数 n, q。

接下来 q 行,每行输入两个或者三个正整数,形如 x y 或 x y w,表示一个操作,相邻的两个数之间用一个空格隔开。

输出格式
对于每个询问操作,你需要输出一行,一个整数,表示这次询问的答案。

数据范围
对于 30% 的数据, 1≤n,q≤30。

对于 60% 的数据,1≤n,q≤3,000。

对于 100% 的数据, 保证 1≤n,q≤300,000 , 1≤w≤103

数据保证,在任何链接操作之前,所链接的两个点 x 与 y 之间不存在任何路径。

样例数据
输入:
4 7
1 3
1 3 100
2 3 200
1 3
1 2
2 3
1 4
输出:
-1
100
300
200
-1
说明:
操作2、3为链接操作,操作1、4、5、6、7为询问操作。
其中1、7操作在询问时没有连通,故答案为 -1

也是打的暴力

每次查询就打一遍spfa,链接就直接连一条边

#include <bits/stdc++.h>
using namespace std;int n,q,x,y,w,dis[300010],vh[300010];
struct nod{int y;int w;nod(int _y,int _w):y(_y),w(_w){}
};
vector<nod> g[300010];struct cmp{bool operator()(int p,int q){return dis[p]>dis[q] || (dis[p]==dis[q] && p>q);}
};
void spfa(int u,int y){priority_queue<int,vector<int>,cmp> myque;memset(dis,0x3f,sizeof(dis));memset(vh,0,sizeof(vh));myque.push(u);vh[u]=0;dis[u]=0;while(!myque.empty()){int x=myque.top();if(x==y)break;myque.pop();vh[x]=0;for(int i=0;i<g[x].size();++i){if(dis[g[x][i].y]>dis[x]+g[x][i].w){dis[g[x][i].y]=dis[x]+g[x][i].w;if(vh[g[x][i].y]==0){myque.push(g[x][i].y);vh[g[x][i].y]=1;}}}}
}
int main(){cin >> n >> q;for(int i=1;i<=q;++i){cin >> x >> y;char a=getchar();if(a==' '){cin >> w;g[x].push_back(nod(y,w));g[y].push_back(nod(x,w));}else{spfa(x,y);if(dis[y]==1061109567)cout << -1 << endl;elsecout << dis[y] << endl;}}return 0;
}

注意这是一棵树!

因为题目说了所链接的两点间不存在任何一条边,所以这是树(比赛时没看到)

Tarjan算法求解LCA即可

可以先用并查集将几棵树链接起来,如果遇到边就存起来,如果遇到询问首先判断两点是否在一棵树中(用并查集找祖先即可),如果不在,则答案记下-1,否则存到询问数组中。显然,这是一片森林,通过并查集可以轻松判断出每棵树的树节点,然后循环每棵树的节点,打Tarjan算法即可。

#include <bits/stdc++.h>
using namespace std;int n,q,x,y,w,fa[300010],ans[300010],dis[300010],vis[300010];
struct nod{int v;int wi;nod(int _v,int _wi):v(_v),wi(_wi){}
};
vector<nod> g[300010];
vector<nod> que[300010];
vector<int> root;int find(int x){if(x!=fa[x])fa[x]=find(fa[x]);return fa[x];
}int f(int x,int v){if(x==v)return v;if(x!=fa[x])fa[x]=f(fa[x],v);return fa[x];
}void uni(int x,int y){int a=find(x);int b=find(y);if(a!=b)fa[b]=a;
}void tarjan(int u,int p){vis[u]=p;for(int i=0;i<g[u].size();++i){int vv=g[u][i].v,ww=g[u][i].wi;if(vis[vv]==p)continue;dis[vv]=dis[u]+ww;tarjan(vv,p);fa[vv]=u;}for(int i=0;i<que[u].size();++i){int vv=que[u][i].v;int id=que[u][i].wi;if(vis[vv]==p){int lca=find(vv);ans[id]=dis[u]+dis[vv]-2*dis[lca];}}
}int main(){cin >> n >> q;for(int i=1;i<=q;++i)ans[i]=-2;for(int i=1;i<=n;++i)fa[i]=i;for(int i=1;i<=q;++i){cin >> x >> y;char c=getchar();if(c==' '){cin >> w;uni(x,y);g[x].push_back(nod(y,w));g[y].push_back(nod(x,w));}else{int a=find(x),b=find(y);if(a==b){que[x].push_back(nod(y,i));que[y].push_back(nod(x,i));}elseans[i]=-1;}}for(int i=1;i<=n;++i)if(i==fa[i])root.push_back(i);for(int i=1;i<=n;++i)fa[i]=i;for(int i=0;i<root.size();++i){dis[root[i]]=0;tarjan(root[i],i+1);}for(int i=1;i<=q;++i){if(ans[i]!=-2)cout << ans[i] << endl;}return 0;
}

上海11月月赛乙组解题报告相关推荐

  1. 上海市计算机学会2022年10月月赛丙组解题报告

    上海市计算机学会2022年10月月赛丙组解题报告 直角三角形的判定 题目描述 给定三个正整数表示三角形的三条边,请判定它是否为直角三角形 输入格式 第一行:三个整数 a,b 与 c 输出格式 若可以构 ...

  2. 上海2022年11月月赛丙组

    上海2022年11月月赛丙组 T1~T4略 T5 出栈序列 题目描述 给定一个长度为 n n n的.仅由小写字母组成的字符串,将其按序依次放入栈中. 请问在所有可能的出栈序列中,字典序最小的出栈序列是 ...

  3. 关于517编程的11月月赛

    关于517编程的11月月赛-Nov.29 by Jasonxu 我是传送门 T1:umin之和 1.1题目 小海狸定义 umin为最小的没有在子集中出现过的非负整数. 小海狸有一组非负整数,他希望将这 ...

  4. csu-2018年11月月赛Round2-div1题解

    csu-2018年11月月赛Round2-div1题解 A(2191):Wells的积木游戏 Description Wells有一堆N个积木,标号1~N,每个标号只出现一次 由于Wells是手残党, ...

  5. 报告解读下载 | 11月《中国数据库行业分析报告》发布,精彩抢先看

    为了帮助大家及时了解中国数据库行业发展现状.梳理当前数据库市场环境和产品生态等情况,从2022年4月起,墨天轮社区行业分析研究团队出品将持续每月为大家推出最新<中国数据库行业分析报告>,持 ...

  6. 【FOJ2207 11月月赛C】【DFS栈性质应用 离线处理】以撒的结合 从x到y路径上的第k个点 询问众多

     Problem 2207 以撒的结合 Accept: 30    Submit: 98 Time Limit: 1000 mSec    Memory Limit : 32768 KB  Probl ...

  7. 上海市计算机学会月赛 2022年7月月赛丙组

    上海市计算机学会月赛 2022年7月月赛丙组 水仙花指数 因数之和 观光电梯 匹配括号(三) 打印六芒星 本文仅供学术探讨 水仙花指数 内存限制: 256 Mb时间限制: 1000 ms 题目描述 定 ...

  8. 安恒赛php_安恒11月月赛周周练writeup

    前言 11月月赛 完美错过时间,正好有周周练,基本都是一样月赛的web,记录下write up 手速要快 这题是10月月赛中的一题,直接看我上次的writeup:安恒月赛(十)web-2题writeu ...

  9. NOIP 2018 普及组 解题报告

    比完小结 今年的题目出的有点诡异,难度跨越有点大 入门 to 普及- to(注意:前方东非大裂谷,请小心慢行) 提高+/省选- to 提高+/省选- 不过实际上没有这么难 T3.T4 一个DP 一个暴 ...

最新文章

  1. AngularJs 基础教程​ —— Select(选择框)
  2. ZULUTrade骗局揭秘--一名福汇员工的良心发现
  3. July面试整理系列--(5)
  4. HDU - 5451 Best Solver(循环群+矩阵快速幂)
  5. 2021 年前端趋势预测
  6. JavaScript_Util_01
  7. [Usaco2014 Open]Gold Fair Photography(hash)
  8. pushViewController自定义动画http://blog.csdn.net/ralbatr/article/details/22039233
  9. 浅谈软件架构师的工作
  10. 解读主流CDN厂商的节点数据
  11. 倍加福二维码测试1-串口助手测试
  12. 凤凰项目-----读书笔记
  13. 青龙面板跑爱企查脚本 兑换爱奇艺月卡 百度网盘会员等
  14. 小特工具箱5.0发布 增加50+个功能
  15. 第三天---随机小方块
  16. java基础实战练习_JAVA入门第三季第七章实战练习
  17. 基于FPGA的SATA 3.0 Host 控制器
  18. MoocTest下载出错解决方案
  19. python-对象类型
  20. 2018数学建模国赛

热门文章

  1. RK3568烧录系统
  2. OFFTIME for Android 断线时间 手机想都别想
  3. java.dll_ibtmjava.dll,下载,简介,描述,修复,等相关问题一站搞定_DLL之家
  4. RK3128 Linux 源代码,firefly rk3128 linux 内核适配问题
  5. oracle小机系统,【案例分享】Oracle系统参数过小导致数据库宕机
  6. python 中画球体_python – 有效地绘制许多球体
  7. SQL Server 2008性能故障排查(三)——I/O
  8. [流畅的Python][8][对象引用、可变性和垃圾回收]
  9. 编写 systemctl 入门
  10. 2021年Java开发爆款推荐!mysql密码修改命令Linux