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的主要内容,如果未能解决你的问题,请参考以下文章

2019.10.22

2019-10-22 R语言Seurat包下游分析-1

2019.10.22模拟赛

2019.10.22 用TCP实现服务端并发接收

2019-10-22

2019.10.22日上午学习内容