2019-10-22
Posted mendessy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019-10-22相关的知识,希望对你有一定的参考价值。
T1
一道很水的题 其实对于本蒟蒻来说也没有那么水 只需要知道最小值总是会在零点的时候取到 因此将零点排序之后枚举当(x=)每个零点时 整个式子的值 然后用ans来存最小值。
需要注意的地方:1.因为我用的方法是用前缀和,后缀和优化,所以一定要将零点排序
2.当(a=0)的时候 零点算不出来。。我是将零点赋为极大值
3.当(a<0)的时候 将(a,b)都取反
因为在零点左边的所有式子一定大于零 在零点右边的式子一定小于零 所以可以用前缀和后缀和来实现
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
const ll maxn=300010;
const ll inf=0x3f3f3f3f;
ll read()
{
ll x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
ll n,prea[maxn],preb[maxn],sufa[maxn],sufb[maxn];
ll a[maxn],b[maxn];
struct node{
ll prea,preb,sufa,sufb,id,a,b;
long double zero;
#define prea(x) job[x].prea
#define preb(x) job[x].preb
#define sufa(x) job[x].sufa
#define sufb(x) job[x].sufb
#define id(x) job[x].id
#define a(x) job[x].a
#define b(x) job[x].b
}job[maxn];
long double ans=999999999999999,now,bj;
inline bool cmp(node x,node y)
{
return x.zero<y.zero || (x.zero==y.zero && x.id<y.id);
}
int main()
{
freopen("spongebob.in","r",stdin);
freopen("spongebob.out","w",stdout);
n=read();
for(ll i=1;i<=n;i++)
{
a(i)=read(),b(i)=read();
if(a(i)<0){
a(i)=-a(i);b(i)=-b(i);
}
else if(a(i)==0){
job[i].zero=0x3f3f3f3f*1.0;
}
job[i].zero=-1.0*(double)b(i)/(double)a(i);
}
sort(job+1,job+1+n,cmp);
for(int i=1;i<=n;i++)
{
prea(i)=prea(i-1)+a(i);
preb(i)=preb(i-1)+b(i);
}
for(ll i=n;i>=1;i--)
{
sufa(i)=sufa(i+1)+a(i);
sufb(i)=sufb(i+1)+b(i);
}
for(ll i=1;i<=n;i++)
{
now=0;
now=job[i].zero*prea(i)*1.0+preb(i)*1.0-job[i].zero*sufa(i)*1.0-sufb(i)*1.0;
if(ans>now) ans=now,bj=job[i].zero;
}
printf("%.5f",(double)ans);
return 0;
}
T2
这道题暴力可以拿(30pts)呢! 所以说为什么我的分块才(20pts)!蒟蒻表示一脸懵逼
那么暴力的方法就不用说了吧 每次都扫描一遍。
然后我们来看正解
首先分析如果对于任意的(i∈[0,n-1]),(h[i] < h[i+ 1]) 均可以对([a[i]+1,a[i+1]])造成一点贡献 当水平线到了([a[i]+1,a[i+1]])时,因为(a[i])小于该区间,(a[i+1])等于该区间 所以当水平线在这个范围内 相当于会新增加一个岛屿 所以最终你需要输出的答案就是当前水平线对应的值是多少
因为这个是待修的 所以可以用线段树和树状数组
那么接下来就上代码啦!
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=600010;
int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int h[maxn],n,m;
int tree[maxn];
int last=0;
int lowbit(int x)
{
return x&(-x);
}
void add(int x,int val)
{
x++;
for(;x<=maxn;x+=lowbit(x))
tree[x]+=val;
}
int query(int x)
{
int ans=0;
for(;x;x-=lowbit(x))
ans+=tree[x];
return ans;
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
h[i]=read();
for(int i=1;i<=n;i++)
{
if(h[i-1]<h[i])
{
add(h[i-1],1);
add(h[i],-1);
}
}
for(int i=1;i<=m;i++)
{
char type;
scanf("%s",&type);
if(type=='Q')
{
int q=read();
q=q^last;
printf("%d
",last=query(q));
}
else
{
int x=read(),y=read();
x=x^last;y=y^last;
if(h[x-1]<h[x])
{
add(h[x-1],-1);add(h[x],1);
}
if(h[x]<h[x+1])
{
add(h[x],-1);add(h[x+1],1);
}
h[x]=y;
if(h[x-1]<h[x])
{
add(h[x-1],1);
add(h[x],-1);
}
if(h[x]<h[x+1])
{
add(h[x],1);
add(h[x+1],-1);
}
}
}
return 0;
}
T3
这道题 我觉得我不会很正常(会了才奇怪呢)
暴力:每一条边都枚举其情况 复杂度(O(2^m),20pts)
正解:首先易证:此题肯定有解!
对于每一个点如果同一个权值的边出现了两次 那么就可以考虑将这两条边删了 加入一条新的边 关于这道题的细节真的很多 具体的话看代码 我tm原地转圈圈懵逼
#include<bits/stdc++.h>
using namespace std;
#define in read()
int in{
int cnt=0,f=1;char ch=0;
while(!isdigit(ch)){
ch=getchar();if(ch=='-')f=-1;
}
while(isdigit(ch)){
cnt=cnt*10+ch-48;
ch=getchar();
}return cnt*f;
}
struct node{
int id,to,d;
}a[2][2000003];
int n,m;
int depend[2000003];
int rev[2000003];
//rev 数组 解决的问题是 在最后的时候 ans[i] 由其 depend的边转换过来时 是否需要取反
int ans[2000003];int tot;
void add(int x,int y,int id,int r){//x,y表示连接的点 id表示正在加入第i条边 r表示权值
node*q=a[r];// q表示所有的 权值为r的边
//q[x].id 表示当前x 的边的编号
//q[x].to 表示当前边指向的点
if(q[x].id&&q[x].to==y){//相当于 两个点 之间连了 两条边 都要删掉
depend[q[x].id]=depend[id]=0;//当前正在加入的边 和 之前该点对应的边 的依靠 为0
ans[id]=1;ans[q[x].id]=q[y].d;//当前边ans 设为 1 之前边 ans 设为 y的上一条边的 方向
q[x].id=q[y].id=0;//把他们的id 设为 0 相当于删边
return;
}
int flag=1;
if(q[x].id){//如果 x 之前有边
flag=0;
depend[q[x].id]=tot;// 那么x之前的边 依靠于新边
rev[q[x].id]=q[x].d;//rev 对于x来说 他之前边的方向 与 其指向相同
//而在实际计算中后面对ans 的计算中 其实是取反了的84行
//因为对于q[x].id这条边来说 他与新加入的边的关系 相同的
//若 d==1 则相当于之前的边是指向 x的
//若 新边的方向为 0 那么 这一条边的方向不变
//否则 会改变
//建议手玩一下
q[x].id=0;//把x 之前的边 删了
x=q[x].to;//将x指向 之前边指向的另一个点
}
//与 x同理
if(q[y].id){
flag=0;
depend[q[y].id]=tot;
rev[q[y].id]=q[y].d^1; //这里与x刚好相反
q[y].id=0;
y=q[y].to;
}
q[x].to=y;q[y].to=x;//互相 成为 对方到达的点
q[x].d=1;q[y].d=0;//钦定方向 默认 从 x -> y的 方向
if(flag)q[x].id=q[y].id=id; //他们的 id 相当于 当前加入的边
else{
q[x].id=q[y].id=depend[id]=tot++;//将他们的 id 设为 新点 并且 将他们的依靠 设为新边
//至于关于depend的疑惑 实际在 38 和 42行就已经处理过了
}
//相当于 如果 flag == 0 就先当于 加边之后有环了 所以可以将xy对应的之前的边以及当前加入的边 依靠在新边上
}
int vis[2000003];
void work(int u){
int x=u,t=0;
vis[x]=1;
//t是权值 x是点 a[t][x]相当于 x点在权值t下 对应的唯一一条边
//这里相当于从边权为1的边开始走
while(a[t][x].id){
ans[a[t][x].id]=a[t][x].d;//先 将当前边的ans暂且的设为该边本身的方向
if(vis[a[t][x].to])return;//如果出现环了 就返回 因为继续走下去 他还是个环。
// 不用担心ans没更新完 因为主函数里面 是一个for 循环
vis[a[t][x].to]=1;
x=a[t][x].to;t^=1;
}
//这里相当于从边权为2的边开始走
x=u;t=1;
while(a[t][x].id){
ans[a[t][x].id]=a[t][x].d^1;
if(vis[a[t][x].to])return;
vis[a[t][x].to]=1;
x=a[t][x].to;t^=1;
}
}
signed main(){
n=in;m=in;tot=m+1;
for(int i=1;i<=m;i++){
int a=in;int b=in;int c=in;
add(a,b,i,c-1);//第i条边 对应c-1的权值 连接的是a,b
}
for(int i=0;i<n;i++)if(!vis[i])work(i);
for(int i=tot;i>0;i--){
if(depend[i])ans[i]=ans[depend[i]]^rev[i];//rev 如果为1 就要取反 否则 就不取反
}
for(int i=1;i<=m;i++)cout<<ans[i];
return 0;
}
以上是关于2019-10-22的主要内容,如果未能解决你的问题,请参考以下文章