[CSP-S模拟测试92]题解
A.数列
显然每个数的答案是互相独立的,直接扩欧求解。我们需要最小化$ax+by=gcd(a,b)$中的$|x|+|y|$,而显然当x或y靠近0时答案可能最优,列个不等式求一下即可。
能$O(1)$千万不要懒。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
const int N=1e5+5;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
typedef long long ll;
int n;
ll s[N];
ll x,y,A,B;
ll gcd(ll a,ll b)
{
if(!b)return a;
return gcd(b,a%b);
}
ll abss(ll x)
{
return x>0?x:-x;
}
void exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1;y=0;return ;
}
exgcd(b,a%b,x,y);
ll t=x;x=y;y=t-a/b*y;
}
void test()
{
A=read();B=read();int G=gcd(A,B);
cout<<G<<endl;
exgcd(A,B,x,y);
cout<<x<<' '<<y<<endl;
}
int main()
{
//freopen("array.in","r",stdin);
n=read();A=read();B=read();
for(int i=1;i<=n;i++)
s[i]=read();
ll ans=0;ll G=gcd(A,B),ta=A/G,tb=B/G;
exgcd(A/G,B/G,x,y); for(int i=1;i<=n;i++)
{
if(s[i]%G!=0)
{
puts("-1");
exit(0);
}
ll res=1e15; ll t=s[i]/G;
ll nowx=x*t,nowy=y*t;
res=min(res,abss(nowx)+abss(nowy));
ll p=-nowx/tb;
res=min(res,abss(nowx+p*tb)+abss(nowy-p*ta));
res=min(res,abss(nowx+(p-1)*tb)+abss(nowy-(p-1)*ta));
res=min(res,abss(nowx+(p+1)*tb)+abss(nowy-(p+1)*ta));
p=-nowy/ta;
res=min(res,abss(nowx-p*tb)+abss(nowy+p*ta));
res=min(res,abss(nowx-(p-1)*tb)+abss(nowy+(p-1)*ta));
res=min(res,abss(nowx-(p+1)*tb)+abss(nowy+(p+1)*ta)); //cout<<nowx<<' '<<nowy<<endl;
ans+=res;
}
printf("%lld\n",ans);
return 0;
}
B.数对
可任意排序看似难以解决,但考虑一下$a$和$b$之间的限制,不难发现如果$a_i<b_j \ \ and\ \ b_i<a_j$,$i$应当尽可能在$j$前面,如果恰好相反那么$i$就应该在后面,至于剩下的情况,怎么排都是一样的。那么按$a+b$排序就能保证选取一定最优。
之后就是原题了。线段树维护dp板子。
(忽然发现我好像没写过之前那题的题解 懒癌发作失败)
那么现在问题就转化成了给你一个二元组序列,要求你按顺序选几个二元组,在满足所有$a_i \leq b_j \ (i<j)$的情况下最大化权值。
设$dp[i][j]$表示当前考虑到i并且选了它,且所选集合中最大的$a[]$值为j的最大收益。($a[]$和$b[]$当然是要离散化的= =)
暴力$O(n^3)$转移就很显然了:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=1e5+5;
typedef long long ll;
int n,a[N],b[N],S; ll w[N],dp[2505][2505],ans;
vector<int> c;
struct node
{
int a,b;ll w;
}s[N];
bool cmp(node x,node y)
{
if(max(x.b,x.a)!=max(y.b,y.a))
return max(x.b,x.a)<max(y.b,y.a);
else return x.b<y.a;
}
int main()
{
//freopen("pair.in","r",stdin);
n=read();
for(int i=1;i<=n;i++)
s[i].a=read(),s[i].b=read(),s[i].w=read();
sort(s+1,s+n+1,cmp);
for(int i=1;i<=n;i++)
a[i]=s[i].a,b[i]=s[i].b,c.push_back(a[i]),c.push_back(b[i]),w[i]=s[i].w;
sort(c.begin(),c.end());vector<int>::iterator it=unique(c.begin(),c.end());
for(int i=1;i<=n;i++)
{
a[i]=lower_bound(c.begin(),it,a[i])-c.begin()+1;
b[i]=lower_bound(c.begin(),it,b[i])-c.begin()+1;
S=max(S,max(a[i],b[i]));
}
for(int i=1;i<=n;i++)
{
dp[i][a[i]]=w[i];
for(int j=1;j<i;j++)
for(int k=1;k<=b[i];k++)
dp[i][max(k,a[i])]=max(dp[i][max(k,a[i])],dp[j][k]+w[i]),ans=max(ans,dp[i][max(a[i],k)]);
} cout<<ans<<endl;
return 0;
}
然后你发现可以直接把第二维扔到线段树上维护,区间修改区间查询单点更新即可。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
} const int N=1e5+5;
typedef long long ll;
int n,a[N],b[N],S; ll w[N],dp[N],ans;
vector<int> c;
struct node
{
int a,b;ll w;
}s[N];
bool cmp(node x,node y)
{
return (x.a+x.b)<(y.a+y.b);
} ll maxv[N<<3],lz[N<<3];
#define ls(k) (k)<<1
#define rs(k) (k)<<1|1
#define up maxv[k]=max(maxv[ls(k)],maxv[rs(k)])
void down(int k)
{
if(!lz[k])return ;
lz[ls(k)]+=lz[k];
lz[rs(k)]+=lz[k];
maxv[ls(k)]+=lz[k];
maxv[rs(k)]+=lz[k];
lz[k]=0;
}
void update(int k,int l,int r,int pos,ll val)
{
if(l==r)
{
maxv[k]=max(maxv[k],val);
return ;
}
down(k);
int mid=l+r>>1;
if(pos<=mid)update(ls(k),l,mid,pos,val);
else update(rs(k),mid+1,r,pos,val);
up;
}
ll ask(int k,int l,int r,int L,int R)
{
if(l>r)return 0;
if(L<=l&&R>=r)return maxv[k];
down(k);
int mid=l+r>>1;ll res=0;
if(L<=mid)res=max(res,ask(ls(k),l,mid,L,R));
if(R>mid)res=max(res,ask(rs(k),mid+1,r,L,R));
return res;
}
void change(int k,int l,int r,int L,int R,ll val)
{
if(l>r)return ;
if(L<=l&&R>=r)
{
maxv[k]+=val;
lz[k]+=val;
return ;
}
down(k);
int mid=l+r>>1;
if(L<=mid)change(ls(k),l,mid,L,R,val);
if(R>mid)change(rs(k),mid+1,r,L,R,val);
up;
} int main()
{
//freopen("pair.in","r",stdin);
n=read();
for(int i=1;i<=n;i++)
s[i].a=read(),s[i].b=read(),s[i].w=read();
sort(s+1,s+n+1,cmp);
for(int i=1;i<=n;i++)
a[i]=s[i].a,b[i]=s[i].b,c.push_back(a[i]),c.push_back(b[i]),w[i]=s[i].w;
sort(c.begin(),c.end());vector<int>::iterator it=unique(c.begin(),c.end());
for(int i=1;i<=n;i++)
{
a[i]=lower_bound(c.begin(),it,a[i])-c.begin()+1;
b[i]=lower_bound(c.begin(),it,b[i])-c.begin()+1;
S=max(S,max(a[i],b[i]));
}
for(int i=1;i<=n;i++)
{
if(a[i]>b[i])
dp[i]=max(dp[i],ask(1,1,S,1,b[i])+w[i]);
else
{
dp[i]=max(dp[i],ask(1,1,S,1,a[i])+w[i]);
change(1,1,S,a[i],b[i],w[i]);
}
update(1,1,S,a[i],dp[i]);
} cout<<ask(1,1,S,1,S)<<endl;
return 0;
}
C.最小距离
什么?dj可以跑多源最短路?
一开始把所有特殊点都扔到堆里,转移的时候记录该点由哪个特殊点转移过来,最后枚举边把答案拼一下就好了。
因为每个点只会被更新一次,所以跑出来每个点的$dis[]$必然为离它最近的那个特殊点的距离。枚举边的时候,如果两端不是同一个点转移而来,就可以用两个$dis[]$+边权更新答案。
#include<bits/stdc++.h>
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return x*f;
}
#define pa pair<ll,int>
const int N=2e5+5;
typedef long long ll;
int n,m,p;
bool isp[N];
int to[N<<1],head[N],nxt[N<<1],vis[N],sp[N],tot,fr[N];
ll w[N<<1],ans[N<<1],dis[N];
void add(int x,int y,ll z)
{
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
w[tot]=z;
}
void dj()
{
priority_queue<pa> q;
for(int i=1;i<=n;i++)
dis[i]=1e15,vis[i]=0;
for(int i=1;i<=p;i++)
q.push(make_pair(0,sp[i])),fr[sp[i]]=sp[i],dis[sp[i]]=0;
while(!q.empty())
{
int x=q.top().second;q.pop();
if(vis[x])continue;
vis[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(dis[y]>dis[x]+w[i])
dis[y]=dis[x]+w[i],fr[y]=fr[x],q.push(make_pair(-dis[y],y));
}
}
for(int i=1;i<=m;i++)
{
int x=to[i*2-1],y=to[i*2];
if(fr[x]!=fr[y])
{
ans[fr[x]]=min(ans[fr[x]],dis[x]+w[i*2]+dis[y]);
ans[fr[y]]=min(ans[fr[y]],dis[x]+w[i*2]+dis[y]);
}
}
}
int main()
{
n=read();m=read();p=read();
for(int i=1;i<=p;i++)
{
int x=read();
sp[i]=x;
isp[x]=1;
}
for(int i=1;i<=n;i++)
ans[i]=1e15;
for(int i=1;i<=m;i++)
{
int x=read(),y=read();ll z=read();
add(x,y,z);add(y,x,z);
}
dj();
for(int i=1;i<=p;i++)
printf("%lld ",ans[sp[i]]);
return 0;
}
[CSP-S模拟测试92]题解的更多相关文章
- CSP-S 模拟测试92 题解
话说我怎么觉得我没咕多长时间啊,怎么就又落了20多场题解啊 T1 array: 根据题意不难列出二元一次方程,于是可以用exgcd求解,然而还有一个限制条件就是$abs(x)+abs(y)$最小,这好 ...
- csp-s模拟测试92
csp-s模拟测试92 关于$T1$:最短路这一定建边最短路. 关于$T2$:傻逼$Dp$这一定线段树优化$Dp$. 关于$T3$:最小生成树+树P+换跟一定是这样. 深入(?)思考$T1$:我是傻逼 ...
- [考试反思]1029csp-s模拟测试92:弱智
我只能这么评价我自己. 看这个提交时间...我没话可说... T1半个世界都A了还是切不掉.又一次挂细节. T2不会证明的乱搞(虽然可以证明)A了没什么可说的算是水过. T3之前水过的题(打的次正解) ...
- CSP-S 模拟测试94题解
T1 yuuustu: 可以对两边取对数,然后就转化为两个double的比较,时间复杂度$O(n)$ 然后我就用神奇0.4骗分水过 #include<bits/stdc++.h> usin ...
- CSP-S模拟测试 88 题解
T1 queue: 考场写出dp柿子后觉得很斜率优化,然后因为理解错了题觉得斜率优化完全不可做,只打了暴力. 实际上他是可以乱序的,所以直接sort,正确性比较显然,贪心可证,然后就是个sb斜率优化d ...
- CSP-S 模拟测试57题解
人生第一次A,B层一块考rank2,虽然说分差没几分,但还是值得纪念. 题解: T1 天空龙: 大神题,因为我从不写快读也没有写考场注释的习惯,所以不会做,全hzoi就kx会做,kx真大神级人物. T ...
- CSP-S 模拟测试 51 题解
考试过程: 惯例先看一遍三道题,T1 一开始反应要求割点,但是这是有向图,肯定不能求割点,康了一下数据范围,有40%是树的,还不错,决定待会在打. 看T2 字符串题,完了我字符串最弱了,肯定只能打暴力 ...
- CSP-S 模拟测试 45 题解
由于咕掉的题解太多了,所以只能趁改完不动题的时间,来补补坑qwq,还是太弱了. 考试过程: 到新机房的第一次考试,貌似海星? 第一题一开始就觉得是个贪心,但以为所有小怪兽都要打完,所以想复杂了,但后来 ...
- [CSP-S模拟测试97]题解
A.小盆友的游戏 感觉题解解释的很牵强啊……还是打表找规律比较靠谱 对于每个人,它构造了一个期望函数$f(x)$,设它的跟班个数为$cnt[x]$,那么令$f(x)=2^{cnt[x]}-1$(??鬼 ...
随机推荐
- [转载]OpenSSL身份认证 RSA、ECC、SM2
一.生成证书openSSL生成RSA证书1 生成自签CA 生成CA密钥genrsa -aes256 -passout pass:123456 -out ca_rsa_private.pem 20481 ...
- Dnsmasq 安装配置
Dnsmasq 官网 http://www.thekelleys.org.uk/dnsmasq/doc.html Unbuntu 安装 Dnsmasq 参见https://help.ubuntu.c ...
- git_04_回退到上个版本
前言 使用git版本控制的过程中,多人操作同一个项目时,有时经常会遇到代码冲突报错,一时又无法解决的问题,为了不影响他人正常使用这时便需要回滚代码至原来的版本.如何回滚代码至原来版,可参考以下步骤. ...
- Mac013--Docker安装
一.Docker安装教程 参考:http://www.runoob.com/docker/macos-docker-install.html 可应用brew命令安装,也可自定义下载安装. 应用brew ...
- 20190903 On Java8 第十七章 文件
第十七章 文件 在Java7中对 文件的操作 引入了巨大的改进.这些新元素被放在 java.nio.file 包下面,过去人们通常把nio中的n理解为new即新的io,现在更应该当成是non-bloc ...
- FileSystemObject详解
FSO是FileSystemObject 或 Scripting.FileSystemObject 的缩写,为 IIS 内置组件,用于操作磁盘.文件夹或文本文件.FSO 的对象.方法和属性非常的多,这 ...
- [Python3] 014 集合的内置方法
目录 1. Python3 中如何查看 set() 的内置方法 2. 少废话,上例子 (1) add() (2) 又见清理大师 clear() (3) 又见拷贝君 copy() (4) 找茬君 dif ...
- hive Hbase sql
Hive和HBase的区别 hive是为了简化编写MapReduce程序而生的,使用MapReduce做过数据分析的人都知道,很多分析程序除业务逻辑不同外,程序流程基本一样.在这种情况下,就需要h ...
- 微信小程序 Mustache语法详解
最近微信小程序非常火,对于前端开发的程序员是个利好的消息,这里主要记录下微信小程序 Mustache语法. 小程序开发的wxml里,用到了Mustache语法.所以,非常有必要把Mustache研究 ...
- [2019杭电多校第五场][hdu6629]string matching(扩展kmp)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6629 题意求字符串的每个后缀与原串的最长公共前缀之和. 比赛时搞东搞西的,还搞了个后缀数组...队友一 ...