2019年8月1日(基本功练习2)

Posted alanallen21love28

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019年8月1日(基本功练习2)相关的知识,希望对你有一定的参考价值。

时间不够了,\(T2\)没切,好烦,T_T……

prob1:Reign

考虑分割为间隔为\(k\)的两个区间,分别跑最大子段和,然后移动中间的\(k\)区间,左边加入,右边弹出,\(O(n)\)得解(\(md\),最大子段和竟然调了一个多小时,我是要退役了吗):

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;++i)
#define fdr(i,a,b) for(int i=a;i>=b;--i)
#define int long long
#define jinitaimei signed
const int xx=1e5+10;
const int inf=1e18;
struct lineint ff,f;le[xx];
int a[xx];
priority_queue<int>q,del,p;
jinitaimei main()

    int t;
    scanf("%lld",&t);
    while(t--)
    
        while(!q.empty()) q.pop();
        while(!del.empty()) del.pop();
        while(!p.empty()) p.pop();
        int n,k;
        scanf("%lld%lld",&n,&k);
        memset(a,0,sizeof(a));
        fur(i,1,n) scanf("%lld",&a[i]);
        fur(i,0,n+1) le[i].ff=le[i].f=-inf;
        le[1].ff=le[1].f=a[1];p.push(a[1]);
        fdr(i,n,2+k)
        
            if(le[i+1].ff>=0) le[i].f=le[i+1].ff+a[i];
            else le[i].f=a[i];
            if((a[i]<0&&le[i+1].ff+a[i]>=0)||(a[i]>=0&&le[i+1].ff>=0)) le[i].ff=le[i+1].ff+a[i];
            else le[i].ff=a[i];
            q.push(le[i].f);
        
        int ans=p.top()+q.top();
        fur(i,2,n-k-1)
        
            del.push(le[i+k].f);
            while(!del.empty()&&!q.empty()&&del.top()==q.top()) del.pop(),q.pop();
            if(le[i-1].ff>=0) le[i].f=le[i-1].ff+a[i];
            else le[i].f=a[i];
            if((le[i-1].ff+a[i]>=0&&a[i]<0)||(a[i]>=0&&le[i-1].ff>=0)) le[i].ff=le[i-1].ff+a[i];
            else le[i].ff=a[i];
            p.push(le[i].f);
            ans=max(ans,p.top()+q.top());
        
        printf("%lld\n",ans);
    
    return 0;

prob6:Chef and Digit Jumps

连边,左右两点直接连,等值节点菊花连,跑最短路即可(这里用的配对堆):

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;i++)
#define nm make_pair
#define ll long long
#define pa pair<ll,int>
#define heep __gnu_pbds::priority_queue<pa,greater<pa> >
inline int read()

    int x=0,f=1;char ch=getchar();
    for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;

const int xx=1e5+20;
int dis[xx],n;
char ch[xx];
vector<pair<int,int> >e[xx];
heep q;
heep::point_iterator id[xx];
const int inf=1e7;
inline void dij()

    fur(i,2,n+11) dis[i]=inf;
    id[1]=q.push(nm(0,1));
    while(!q.empty())
    
        int u=q.top().second;q.pop();
        fur(i,0,(int)e[u].size()-1)
        
            if(e[u][i].second+dis[u]<dis[e[u][i].first])
            
                dis[e[u][i].first]=dis[u]+e[u][i].second;
                if(id[e[u][i].first]!=0) q.modify(id[e[u][i].first],nm(dis[e[u][i].first],e[u][i].first));
                else id[e[u][i].first]=q.push(nm(dis[e[u][i].first],e[u][i].first));
            
        
    

int main()

    scanf("%s",ch+1);
    n=strlen(ch+1);
    fur(i,1,n)
    
        ch[i]=ch[i]-'0'+1;
        e[n+(int)ch[i]].push_back(nm(i,0));
        e[i].push_back(nm(n+ch[i],1));
        if(ch[i-1]!=ch[i]&&i!=1) e[i].push_back(nm(i-1,1));
        if(ch[i+1]!=ch[i]&&i!=n) e[i].push_back(nm(i+1,1));
    
    dij();
    printf("%d\n",dis[n]);
    return 0;

prob3:Strongly Connected City

题目大意:共\(n\)条横向有向道路与\(m\)条纵向有向道路,形成\(n* m\)个路口,问是否任意两个城市间两两可达(\(n,m<=20\)
数据范围小啊,直接暴力搞啊,\(floyed\)大法好啊,长命没烦恼啊:

#include<iostream>
#include<cstdio>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
inline int read()

    int x=0,f=1;char ch=getchar();
    for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;

const int xx=410;
bool dis[xx][xx];
int main()

    int n=in,m=in;
    char op[21];
    scanf("%s",op+1);
    fur(i,1,n)
    
        if(op[i]=='>') fur(j,1,m) fur(k,j,m) dis[j+(i-1)*m][k+(i-1)*m]=true;
        else fur(j,1,m) fur(k,1,j) dis[j+(i-1)*m][k+(i-1)*m]=true;
    
    scanf("%s",op+1);
    fur(i,1,m)
    
        if(op[i]=='v') fur(j,1,n) fur(k,j,n) dis[i+(j-1)*m][i+(k-1)*m]=true;
        else fur(j,1,n) fur(k,1,j) dis[i+(j-1)*m][i+(k-1)*m]=true;
    
    fur(k,1,n*m) fur(i,1,n*m) fur(j,1,n*m) if(dis[i][k]&&dis[k][j]) dis[i][j]=true;
    fur(i,1,n*m) fur(j,1,n*m) if(!dis[i][j]) puts("NO"),exit(0);
    puts("YES");
    return 0;

prob2:trips

考虑反向跑,每次都删去度小于\(k\)的点(对点标记),删去与其相连的边(同样对边进行标号标记即可),(这里使用小根配对堆维护度的取出):

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;++i)
#define fdr(i,a,b) for(int i=a;i>=b;--i)
#define pa pair<int,int>
#define heep __gnu_pbds::priority_queue<pa,greater<pa> >
#define nm make_pair
inline int read()

    int x=0,f=1;char ch=getchar();
    for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;

const int xx=2e5+10;
int du[xx],cnt=0;
vector<pa>e[xx];
struct edgeint u,v,ans;bool use;d[xx];
heep p;
heep::point_iterator id[xx];
bool cut[xx];
int main()

    int n=in,m=in,k=in;
    fur(i,1,m)
    
        int x=in,y=in;
        d[i].u=x,d[i].v=y;
        e[x].push_back(nm(y,i));
        e[y].push_back(nm(x,i));
        ++du[x];++du[y];
    
    fur(i,1,n) id[i]=p.push(nm(du[i],i));
    cnt=n;
    fdr(i,m,1)
    
        while(!p.empty()&&p.top().first<k)
        
            int u=p.top().second;p.pop();
            cut[u]=true;du[u]=0;
            --cnt;
            fur(j,0,(int)e[u].size()-1)
            
                if(cut[e[u][j].first]) continue;
                if(d[e[u][j].second].use) continue;
                d[e[u][j].second].use=true;
                --du[e[u][j].first];
                p.modify(id[e[u][j].first],nm(du[e[u][j].first],e[u][j].first));
            
        
        d[i].ans=cnt;
        if(!d[i].use)
        
            if(cut[d[i].u]||cut[d[i].v]) continue;
            --du[d[i].u];
            --du[d[i].v];
            p.modify(id[d[i].u],nm(du[d[i].u],d[i].u));
            p.modify(id[d[i].v],nm(du[d[i].v],d[i].v));
            d[i].use=true;
        
    
    fur(i,1,m) printf("%d\n",d[i].ans);
    return 0;

prob4:Reach Equilibrium

小凯的疑惑级的数学。

先看合法的充要条件:选出的\(n\)个向量的模均小于它们的和的一半\(k/2\),设向量模为\(a_i\),则为\(\forall_i \in na_i<k/2\).

这样的概率不好直接求,考虑补集思想,计算不构成的概率,用一减之。

即求\(a_i>=k/2\)的概率。

由于当\(a_i>=k/2\)时,满足\(\forall_j \in n \land j \ne ia_j<k/2\),故\(n\)个模长大于等于\(k/2\)的事件是互相独立的。

\(so,what~we~want~can~be~P(\exists_i \in na_i>=k/2)~which~can~be~expressed~that~n* P(a_i>=k/2)(i \in n)\)

考虑求\(P(a_i>=k/2)\),直接硬掰不美,考虑几何概型。

在长度为\(len\)的线段上取\(n\)条线段其实是在长度为\(len\)的线段上取\(n-1\)个点分割该线段,由于取线段的概率相等,故点落在线段上的任意位置的概率相等。而\(n\)条线段中有一条的模大于等于\(k/2\)的充要条件是这\(n-1\)个点全部落在\(len\)线段的一边。其中一个点落在指定一边的概率为\(\dfrac12\),则\(n-1\)个点全部落在指定一边的概率为\(\dfrac12^n-1\),即\(P(a_i>=k/2)=\dfrac12^n-1\),带入之前得到的公式,则有:
\[P(\exists_i \in na_i>=k/2)=\dfracn2^n-1\]
题目所求为:
\[ans\equiv (1-\dfracn2^n-1)(mod~1e9+7)\]
式子难推,妹子难追,所幸,代码清真:

#include<iostream>
#include<cstdio>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;i++)
#define int long long
#define jinitaimei signed
inline int read()

    int x=0,f=1;char ch=getchar();
    for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;

const int mod=1e9+7;
inline int power(int x,int k)int res=1;for(;k;k>>=1,x=x*x%mod) if(k&1) res=res*x%mod;return res;
jinitaimei main()

    int n=in,k=in;
    printf("%lld\n",(power(2,n-1)-n+mod)%mod*power(2,(n-1)*(mod-2))%mod);
    return 0;

prob5:Partition into Permutations

正解是双向链表,可惜看不懂。像上次双向链表准备摧残我幼小的灵魂时,\(yyz\)大仙用\(yyz\)神仙队列救了我一样,\(wz\)大仙在双向链表的恐怖威压下一波\(DP\)秒切该题,将复杂的代码给压成了\(44\)行的清真代码

\(f_i,j\) 表示当前指向了数字\(i\),并且\(i\)数字保留\(j\)个的最少步数;\(h_i\)表示\(i\)数字在原序列中的出现次数。则状态转移方程为:
\[f_i,j=min(\forall_k>=j \land k<=max(\forall_q \in nh_q)f_i,k)+abs(j-h_i)\]
但是空间开不下,于是考虑可将\(f\)数组降一维。

但输入的\(a_i\)范围很大(\(1e9\)),依旧很大。但仔细思考后,显然有当\(a_i>2*n\)时,删去\(a_i\)必定更优。将\(a_i>2*n\)的数量给计入\(basic\),然后不管即可。

目标:\(min(\forall_i>=0 \land i<=max(\forall_q \in nh_q)f_i)+basic\)(式子中已降维)

同理,游戏结束:

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define in read()
#define fur(i,a,b) for(int i=a;i<=b;++i)
#define fdr(i,a,b) for(int i=a;i>=b;--i)
#define int long long
inline int read()

    int x=0,f=1;char ch=getchar();
    for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;

const int xx=1e6+10;
int f[xx<<1];
int h[xx<<1];
signed main()

    int t=in;
    while(t--)
    
        int n=in,basic=0,mx=0,mxx=0;
        fur(i,1,n)
        
            int a=in;
            if(a<=n*2) mx=max(mx,a),h[a]++;
            else basic++;
        
        fur(i,1,mx) mxx=max(mxx,h[i]);
        fur(i,0,mxx) f[i]=abs(h[1]-i);
        fur(i,2,mx)
        
            fdr(j,mxx-1,0) f[j]=min(f[j],f[j+1]);
            fur(j,0,mxx) f[j]+=abs(h[i]-j);
        
        int ans=f[0];
        fur(i,1,mxx) ans=min(ans,f[i]);
        printf("%lld\n",ans+basic);
        fur(i,0,mx) h[i]=0;
    
    return 0;

以上是关于2019年8月1日(基本功练习2)的主要内容,如果未能解决你的问题,请参考以下文章

2019年7月31日(基本功练习1)

2019年 8月 10日训练日记

2019年12月8日 SPSS 运飞龙

2019年8月5日训练日记

linux7.2基本命令操作练习

2020年8月2日 方法的传递机制(参数是基本数据类型参数是引用数据类型)