比赛经过:写了七八题,有一个topsort写错地方了,本场题目都较为简单考的知识都比较明显
补题:有些题目还得多思考其实也不难
目录
B.阿强的路
floyd变式
考虑到数据范围图论知识可以联想到floyed,注意题目要的结果是两点之间路径中的最大点权*最大边权最小,我们对于路径a-b就是要找到用了哪些点,跑了哪些边,由于floyd的性质本质就是dp,在用前k个点的时候的最短路,我们不妨按照点权排序去跑第k层,对于当前层次如果边权比我的大,说明在之前有一个边权更小的同时点权最小的答案当前不需要更新答案,本题重复考察了floyd的性质理解
int w[M];
LL g[M][M],f[M][M]; // 两边之间的最短路的最大边权和 两点之间最短路的最大边权*最大点权
struct code{
int w,id;
bool operator<(const code&t)const{
return w<t.w;
}
}e[N];
void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>w[i];
e[i]={w[i],i};
}
sort(e+1,e+1+n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=g[i][j]=2e18;
while(m--){
int a,b;LL c; cin>>a>>b>>c;
g[a][b]=g[b][a]=min(g[a][b],c);
f[a][b]=f[b][a]=min(f[a][b],g[a][b]*max(w[a],w[b]));
}
for(int k=1;k<=n;k++)// 按照点权来排序使用最大的边权
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(g[i][j]>max(g[i][e[k].id],g[e[k].id][j])){ // 当前的大于 [i,e[k].id] 和 [e[k].id,j]
g[i][j] = max(g[i][e[k].id],g[e[k].id][j]);
f[i][j] = min(f[i][j],g[i][j]*max({w[i],w[j],e[k].w}));
}
// 由于我们是按照点权去跑的k如果当前的百年大于i,j的话说明前面有更小的点和更小的边权
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) f[i][j]=0;
if(f[i][j]>1e18) f[i][j]=-1;
cout << f[i][j] << ' ';
}
cout << endl;
}
return ;
}
C.传染病统计
暴力
看起来我们需要思考一下性质考虑并查集啥的但是注意到数据范围,我们直接排序之后对于每一个数一直左右跑即可
int a[15];
void solve(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+1+n);
int smax = 0, smin = n;
for(int i=1;i<=n;i++){
int j= i+1;
int cnt = 1;
while(j<=n and a[j]-a[j-1]<=2){
cnt++;
j++;
}
j=i-1;
while(j>=1 and a[j+1]-a[j]<=2){
cnt++;
j--;
}
smax=max(smax,cnt);
smin=min(smin,cnt);
}
cout << smin << ' ' << smax << endl;
return ;
}
D.阿强与网格
数学小推理
对于数据范围我们可以发现肯定是要推出一些小式子的在o(1)的时间复杂度解决这个问题
对于特例可以直接判断,重合的,只有一条线的,我们可以作图模拟一下,当我的点没有抵达n,m同行或者同列的时候从可以直接跳min(n,m)-1到同一层,接着发现如果(n+m)是偶数的话是可以直接跳过去的否则就是必须走一步,对于到了同一层之后我们发现后面的跳和走步数是一样的就是做差,最优解就是 1.一直走过去 2.一直跳过去 3.跳过去之后走
int t,n,m,x,y;
void solve(){
cin>>n>>m>>x>>y;
if(n==1 and m==1){ // 重合的点直接输出答案
cout << 0 << endl;
return ;
}
LL res1 = (LL)(n-1+m-1)*x; // 直接一步一步
if(n==1 or m==1){ // 无法跳跃
cout << res1 << endl;
return ;
}
int ok = (n+m)%2;
if(m>n) swap(n,m);
int X = 1 + m-1 ,Y = m;
LL res2 = (LL)(m-1)*y;
LL cnt = n - X; // 步数
if(cnt==0){ // 直接跳到了
cout << min(res1,res2) << endl;
return ;
}
if(ok){// 奇数 需要走一步
res2 += min((LL)(cnt-1)*y+x,(LL)cnt*x);
cout << min(res1,res2) << endl;
}
else{
res2 += min((LL)cnt*y,(LL)cnt*x);
cout << min(res1,res2) << endl;
}
return ;
}
E.生活大爆炸
小组合数公式
直接使用小范围组合数公式暴力求解即可
LL C[M][M];
void init(){
for(int i=0;i<M;i++)
for(int j=0;j<=i;j++)
if(!j) C[i][j]=1;
else C[i][j]=C[i-1][j]+C[i-1][j-1];
}
void solve(){
cin>>n>>m>>t;
LL ans = 0;
for(int i=4;i<=n;i++)
for(int j=1;j<=m;j++){
if(i+j==t){
ans += (LL)C[n][i]*C[m][j];
}
}
cout << ans << endl;
return ;
}
F.Capslock
简单小模拟
使用islower(),toupper(),tolower()函数
void solve(){
string s; cin>>s;
n = s.size(); s=' '+s;
int ok1 = islower(s[1]),cnt = 0;
for(int i=1;i<=n;i++){
if(isupper(s[i])) cnt++;
}
if(cnt==n){
for(int i=1;i<=n;i++) s[i]=tolower(s[i]);
}
else if(ok1 and cnt==n-1){
s[1] = toupper(s[1]);
for(int i=2;i<=n;i++) s[i] = tolower(s[i]);
}
for(int i=1;i<=n;i++) cout << s[i];
cout << endl;
return ;
}
G.字节类型
大数比较
比较两个大数,直接先数位后大小即可
string s[N]={"127","32767","2147483647","9223372036854775807"," "};
string ans[N]={"byte","short","int","long","BigInteger"};
void solve(){
auto check = [&](string&a,string&b){
if(a.size()>b.size() or a==b) return true;
if(a.size()<b.size()) return false;
n = a.size();
for(int i=0;i<n;i++){
if(a[i]==b[i]) continue;
return a[i]>b[i];
}
return true;
};
string a; cin>>a;
for(int i=0;i<=4;i++){
if(i==4 or check(s[i],a)){
cout << ans[i] << endl;
return ;
}
}
return ;
}
H.制造游戏币
有限制性的背包
可以发现要求有a的数量一定是大于b,说明,在买a的时候一定是要买b的有一个绑定的性质
我们可以建图来确定绑定关系,明显的如果说有环的话一定是无解的,他那个是注意到对于这个绑定性质可以使用dfs去跑,变成选这个物品要有的真实连带花费
int dp[N],p[N];
LL w[N];
bool in[N];
vector<int> g[N];
int find(int x){
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
void dfs(int u){
for(auto&v:g[u]){
w[v] += w[u];
dfs(v);
}
if(!g[u].empty()) T -= w[u];// 表示严格的大于关系
}
void solve(){
cin>>n>>m>>T;
for(int i=1;i<=n;i++) cin>>w[i],p[i]=i;
bool ok = false;
while(m--){
int a,b; cin>>a>>b;
in[b]=true;
g[a].push_back(b);
int fa = find(a), fb = find(b);
if(fa==fb){
ok = true;
}
else{
p[fa]=fb;
}
}
if(ok){// 表示矛盾了
cout << 0 << endl;
return ;
}
for(int i=1;i<=n;i++) if(!in[i]) dfs(i);
dp[0] = 1;
for(int i=1;i<=n;i++)
for(int j=w[i];j<=T;j++){
dp[j] += dp[j-w[i]];
dp[j] %= mod;
}
cout << dp[T] << endl;
return ;
}
I.完美主义
线段树
明显的直接使用线段树维护即可,bool表示当前区间是否可行,同时维护每个区间的左右端点数用于两个区间结合的时候的判断
int w[N];
struct code{
int l,r;
int lx,rx;
bool ok;
}tr[4*N];
void pushup(code&u,code&l,code&r){
if(!l.ok or !r.ok) u.ok = false;
else{
if(l.rx<=r.lx){
u.ok = true;
}
else{
u.ok = false;
}
}
u.lx = l.lx,u.rx = r.rx;
}
void pushup(int u){
pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r){
if(l==r){
tr[u]={l,r,w[l],w[l],1};
return ;
}
tr[u]={l,r};
int mid=l+r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
void modify(int u,int v ,int x){
if(tr[u].l== v && v==tr[u].r){
tr[u].lx = x ;
tr[u].rx = x;
return ;
}
int mid=tr[u].l+tr[u].r>>1;
if(v<=mid) modify(u<<1,v,x);
if(v>mid) modify(u<<1|1,v,x);
pushup(u);
}
code query(int u,int l,int r){
if(tr[u].l>=l && tr[u].r<=r){
return tr[u];
}
int mid=tr[u].l+tr[u].r>>1;
if(r<=mid) return query(u<<1,l,r);
else if(l>mid) return query(u<<1|1,l,r);
else{
code res;
code ll=query(u<<1,l,r);
code rr=query(u<<1|1,l,r);
pushup(res,ll,rr);
return res;
}
}
void solve(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>w[i];
build(1,1,n);
while(m--){
int op,l,r; cin>>op>>l>>r;
if(op==1){
modify(1,l,r);
}
else{
code t = query(1,l,r);
cout << (t.ok ? "Yes" : "No") << endl;
}
}
return ;
}
L.神奇的回答
简单模拟
void solve(){
int x; cin>>x;
if(x<18) cout << x << endl;
else cout << 18 << endl;
return ;
}
M.比赛!
topsort
按照题目映射一下直接跑topsort即可
int idx;
vector<int> g[N];
int in[N];
int q[N];
bool use[M][M];
void solve(){
cin>>n;
while(n--){
char a,op,b,c;
cin>>a>>op>>b>>c;
if(!mp.count(a)){
mp[a]=++idx;
G[idx]=a;
}
if(!mp.count(b)){
mp[b]=++idx;
G[idx]=b;
}
if(!mp.count(c)){
mp[c]=++idx;
G[idx]=c;
}
int A = mp[a], B = mp[b] , C = mp[c];
in[A]++,in[C]++;
g[B].push_back(A);
g[A].push_back(C);
}
vector<int> ans;
auto topsort = [&](){
queue<int> q;
for(int i=1;i<=idx;i++) if(!in[i]) q.push(i);
while(!q.empty()){
int u = q.front(); q.pop();
ans.push_back(u);
for(auto&v:g[u]){
if(--in[v]==0) q.push(v);
}
}
return (int)ans.size() == idx;
};
if(topsort()){
for(auto&v:ans) cout << G[v];
cout << endl;
}
else{
cout << "No Answer" << endl;
}
return ;
}