2018.10.9模拟考试

看题请戳我!

T1 一道很水的贪心。(然而蒟蒻博主一如既往地没A)

   维护一个小根堆,考虑在每次插入元素时:

   1.若新元素比堆顶小,说明在此时买入必然比在堆顶时买入更优,因此把新元素直接插入堆中。

   2.若新元素比堆顶大,说明在此时卖出必然能获得收益,因此将收益累计入答案中,再把堆顶删除。

     同时还要把新元素两次插入堆中。

     原因:在最优解中,此元素可能并非作为“卖出”的一部分,而是作为“未进行操作”的一部分。插

           入一次可以使此元素从“卖出”状态反悔到“未进行操作”状态。同样,再次插入可以使此元

           素从“未进行操作”状态反悔到“买入”状态,从而保证了所有情况均被考虑。

#include<algorithm>//STL通用算法
#include<bitset>//STL位集容器
#include<cctype>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>//STL双端队列容器
#include<list>//STL线性列表容器
#include<map>//STL映射容器
#include<iostream>
#include<queue>//STL队列容器
#include<set>//STL集合容器
#include<stack>//STL堆栈容器
#include<utility>//STL通用模板类
#include<vector>//STL动态数组容器
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
ll n,ans;
priority_queue<ll,vector<ll>,greater<ll> > que;
signed main()
{
    freopen("trade.in","r",stdin);
    freopen("trade.out","w",stdout);
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++)
    {
        ll u;scanf("%lld",&u);
        if(que.empty()) que.push(u);
        else if(u<que.top()) que.push(u);
        else ans+=u-que.top(),que.pop(),que.push(u),que.push(u);
    }printf("%lld\n",ans);
    return 0;
}

T2 一道第一眼看不出正确算法的题。

   看一眼数据范围,n<=10^5。显然O(nlogn)(吗?)

   推一推式子,发现并没有O(nlogn)的式子出现。

   emmm...算了,还是打部分分吧。

   看一眼部分分,嗯,前4个点暴力O(n^2)就能水过去。

   等下...这中间6个点是怎么回事?n全部相等?m全部相等?

   瞬间茅塞顿开——莫队。

   这告诉我们要多关心部分分中的条件,也许正解就藏在其中。

   没什么好说的,计算式也很好推,唯一要注意的就是要把所有除法替换成乘逆元。

#include<algorithm>//STL通用算法
#include<bitset>//STL位集容器
#include<cctype>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>//STL双端队列容器
#include<list>//STL线性列表容器
#include<map>//STL映射容器
#include<iostream>
#include<queue>//STL队列容器
#include<set>//STL集合容器
#include<stack>//STL堆栈容器
#include<utility>//STL通用模板类
#include<vector>//STL动态数组容器
#define INF 0x3f3f3f3f
#define ll long long
#define MOD 1000000007
using namespace std;
ll T,Q,N,M,siz,fac[100001],inv[100001],blk[100001],ans[100001];
struct uio{
    ll n,m,id;
}qry[100001];
ll qpow(ll x,ll y)
{
    ll tmp=1;while(y)
    {if(y&1) (tmp*=x)%=MOD;
    (x*=x)%=MOD,y/=2;}
    return tmp;
}
ll C(int x,int y)
{
    if(x<y) return 0;
    return fac[x]*inv[y]%MOD*inv[x-y]%MOD;
}
bool cmp(uio x,uio y) {return (blk[x.n]==blk[y.n]? x.m<y.m:blk[x.n]<blk[y.n]);}
void Init()
{
    fac[0]=1;
    for(ll i=1;i<=N;i++) fac[i]=fac[i-1]*i%MOD;
    inv[N]=qpow(fac[N],MOD-2);
    for(ll i=N-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%MOD;
    siz=sqrt(N);
    for(ll i=1;i<=N;i++) blk[i]=(i-1)/siz+1;
    sort(qry+1,qry+1+Q,cmp);
}
signed main()
{
    freopen("sum.in","r",stdin);
    freopen("sum.out","w",stdout);
    scanf("%lld%lld",&T,&Q);
    for(ll i=1;i<=Q;i++)
        scanf("%lld%lld",&qry[i].n,&qry[i].m),
        qry[i].id=i,N=max(N,qry[i].n),M=max(M,qry[i].m);
    Init();
    ll n=1,m=-1,tmp=0;
    for(ll i=1;i<=Q;i++)
    {
        while(n<qry[i].n) ((tmp*=2)-=C(n,m)-MOD)%=MOD,n++;
        while(m<qry[i].m) m++,(tmp+=C(n,m))%=MOD;
        while(m>qry[i].m) (tmp-=C(n,m)-MOD)%=MOD,m--;
        while(n>qry[i].n) n--,((tmp+=C(n,m))*=inv[2])%=MOD;
        //嗯对博客最后一句说的就是它------------^^^^^^^^------------ 
        ans[qry[i].id]=tmp;
    }
    for(ll i=1;i<=Q;i++) printf("%lld\n",ans[i]);
    return 0;
}

T3 一道丧心病狂的题目...

   对于第一种查询,只需求出每行楼盘个数的前缀和即可。可以开2个sum数组分别表示"行前缀和"和"列差分"。

   对于第二种查询,用并查集维护。可以开n个行vector和m个列vector,在插入完成后对每个vector统计。

   具体操作步骤请查看代码注释。

#include<algorithm>//STL通用算法
#include<bitset>//STL位集容器
#include<cctype>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<deque>//STL双端队列容器
#include<list>//STL线性列表容器
#include<map>//STL映射容器
#include<iostream>
#include<queue>//STL队列容器
#include<set>//STL集合容器
#include<stack>//STL堆栈容器
#include<utility>//STL通用模板类
#include<vector>//STL动态数组容器
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
typedef pair<int,int> pr;
int ID,n,m,k,q,sum1[100002],sum2[100002];
int lnsum=0,mgsum,fa[100002];
int ans1[100002],ans2[100002];
struct uio{
    int lx,ly,rx,ry;
}prg[100002];
struct oiu{
    int id,l,r;
    friend bool operator < (const oiu &x,const oiu &y)
    {return (x.l==y.l? x.r<y.r:x.l<y.l);}
};
vector<oiu> row[100002],col[100002];
vector<pr> dsu[100002];
int find(int x) {return (x==fa[x]? x:fa[x]=find(fa[x]));}
void merge(int x,int y)
{
    int xx=find(x),yy=find(y);
    if(xx!=yy) fa[xx]=yy,mgsum++;
}
int main()
{
    freopen("building.in","r",stdin);
    freopen("building.out","w",stdout);
    scanf("%d%d%d%d%d",&ID,&n,&m,&k,&q);
    for(int i=1;i<=k;i++)
    {
        int lx,ly,rx,ry;
        scanf("%d%d%d%d",&lx,&ly,&rx,&ry);
        prg[i]={lx,ly,rx,ry};
        oiu lenx={i,lx,rx},leny={i,ly,ry};
        row[lx].push_back(leny);
        col[ly].push_back(lenx);
        if(lx==rx) sum1[lx]+=(ry-ly+1);//行数量 
        else sum2[lx]++,sum2[rx+1]--;//列差分 
    }
    for(int i=1;i<=n;i++) sort(row[i].begin(),row[i].end());
    for(int i=1;i<=m;i++) sort(col[i].begin(),col[i].end());
    for(int i=1;i<=k;i++)
    {
        int dwn=prg[i].rx+1;//第i条的下面一行 
        if(dwn<=n&&row[dwn].size())//第i条的下面一行作为某些条的上边界 
        {
            oiu range={0,prg[i].ry,m+1};//第i条的右边 
            int tmp=upper_bound(row[dwn].begin(),row[dwn].end(),range)
                    -row[dwn].begin()-1;//l<=ry的第一条 
            for(;tmp>=0&&row[dwn][tmp].r>=prg[i].ly;tmp--)//r>=ly
                dsu[dwn].push_back(make_pair(i,row[dwn][tmp].id));//在dwn行连接 
        }
        int rgt=prg[i].ry+1;//第i条的右边一列 
        if(rgt<=m&&col[rgt].size())//第i条的右边一列作为某些条的左边界 
        {
            oiu range={0,prg[i].rx,n+1};//第i条的下面 
            int tmp=upper_bound(col[rgt].begin(),col[rgt].end(),range)
                    -col[rgt].begin()-1;//l<=rx的第一条 
            for(;tmp>=0&&col[rgt][tmp].r>=prg[i].lx;tmp--)//r>=lx
                dsu[max(prg[i].lx,prg[col[rgt][tmp].id].lx)].push_back(make_pair(i,col[rgt][tmp].id));
                //在 第i条的上边界 与 该条的上边界 的靠下者 连接 
        }
    }
    for(int i=1;i<=n;i++) sum2[i]+=sum2[i-1];//差分->数量 
    for(int i=1;i<=n;i++) sum2[i]+=sum2[i-1],//数量->前缀和 
                          sum1[i]+=sum1[i-1],//数量->前缀和 
                          ans1[i]=sum1[i]+sum2[i];//第一种查询 
    for(int i=1;i<=k;i++) fa[i]=i;
    for(int i=1;i<=n;i++)
    {
        lnsum+=row[i].size();//第i行作为上边界的条的个数 
        for(int j=0;j<dsu[i].size();j++)
            merge(dsu[i][j].first,dsu[i][j].second);//在第i行合并的两条 
        ans2[i]=lnsum-mgsum;//总条数减去合并次数 
    }
    for(int i=1;i<=q;i++)
    {
        int u,v;scanf("%d%d",&u,&v);
        if(!u) printf("%d\n",ans1[v]);
        else printf("%d\n",ans2[v]);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/water-radish/p/9761254.html