NOIP模拟题 2016.10.13 [贪心] [记忆化搜索]

T1:
题意: 给定一个操作序列,问这个数据结构可能是哪些?
模拟即可。记得判空!!因为可能是不知名的神奇数据结构。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
stack <int> sta;
queue <int> que;
priority_queue <int> heap;
int n;
int main()
{
    freopen("qu.in","r",stdin);
    freopen("qu.out","w",stdout);
    scanf("%d",&n);
    bool ans1 = true , ans2 = true , ans3 = true;
    for(int i=1;i<=n;i++)
    {
        int op,x;
        scanf("%d%d",&op,&x);
        if(op==1)
        {
            sta.push(x);
            que.push(x);
            heap.push(x);
        }
        else
        {
            if(ans1)
                if(sta.empty()) ans1 = false;
                else
                {
                    int tmp = sta.top(); sta.pop();
                    if(tmp^x) ans1 = false;
                }
            if(ans2)
                if(que.empty()) ans2 = false;
                else
                {
                    int tmp = que.front(); que.pop();
                    if(tmp^x) ans2 = false;
                }
            if(ans3)
                if(heap.empty()) ans3 = false;
                else
                {
                    int tmp = heap.top(); heap.pop();
                    if(tmp^x) ans3 = false;
                }
        }
    }
    if(ans1) printf("YES");
    else printf("No");
    putchar('\n');
    if(ans2) printf("YES");
    else printf("No");
    putchar('\n');
    if(ans3) printf("YES");
    else printf("No");
    return 0;
}

T2:
题意: 给出n个任务,分别有完成需要的时间和deadline,要让完成时刻与deadline的差的最大值最小。

一开始想的二分答案,后来发现根本没有那么复杂,其实是水题。。。
果断贪心。
可以直接按照deadline排序。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
const LL INF = (1ll<<60);
const int maxn = 100005;
struct Node
{
    LL x,y;
    bool operator < (const Node t) const
    {
        LL t1 = max(-y,t.x-t.y);
        LL t2 = max(-t.y,x-y);
        return x+t1 < t.x+t2;
    }
    inline void read() { scanf(AUTO AUTO , &x,&y); }
}node[maxn];
int n;
bool cmp(const Node a,const Node b) { return a.x-a.y > b.x-b.y; }
LL work_cheat()
{
    sort(node+1,node+n+1,cmp);
    LL ans = -INF;
    LL cur = 0;
    for(int i=1;i<=n;i++)
        cur+=node[i].x , smax(ans,cur-node[i].y);
    return ans;
}
int main()
{
    freopen("ming.in","r",stdin);
    freopen("ming.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) node[i].read();
    sort(node+1,node+n+1);
    LL ans = -INF;
    LL cur = 0;
    for(int i=1;i<=n;i++)
        cur+=node[i].x , smax(ans,cur-node[i].y);
    LL tmp = work_cheat();
    smin(ans,tmp);
    if(ans < 0) ans = 0;
    printf(AUTO,ans);
    return 0;
}

T3:
题意:有m+1棵树,第0棵树是一个节点,第i棵树的生成方式(tr1,tr2,id1,id2,val),代表将第tr1棵树的id1节点 与 第tr2棵树的id2节点用一条val的边连接。
定义F[i]为任意两点的距离之和。
求F[1~m].

前60分直接可以模拟。。。。

记忆化搜索。
由于不需要建树,每一棵树直接可以用给出的生成方式递归地求出。

定义dist[now][to]为第i棵树所有节点到to节点的距离之和。
显然F[now] = F[i] + F[j] + size[i]*dist[j] [pj]+ size[j]*dist[i] [pi]+ size[i]*size[j]*val

size可以先预处理得到,那么现在需要的就是如何求出dist[now][to]

考虑当前处理的树now , 其连接了 l 和 r 的 id1和id2 节点。
那可以分成两种情况:

  • to在l上。那么答案 = dist[l][to] + dist[r][id2] + (len[l][to][id1] + val) * size[r], 每一个r的节点都要通过这条val的边 和 p1到to的路径。递归求解。
  • to在r上同理,注意处理标号。

其中len[now][p1][p2]表示在now这棵树中p1到p2的距离。

那么如何求出这个距离?

同样递归。

  • p1和p2都在l上。 直接递归到l子树上。
  • 都在r上同理,注意标号。
  • p1在l上p2在r上。分成三段,递归处理len[l][p1][id1],递归处理len[r][p2][id2],再加上val
  • 同理,注意标号。

记忆化就在于len和dist的快速查询,由于节点个数很大,直接开会爆,并且其中有很多无效状态,那么用map可以轻松解决。

具体看代码,可以先把LL和mod去掉,可读性更强233333

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
typedef long long LL;
const int mod = 1000000007;
const int maxm = 65;
struct Node
{
    int tr1,tr2;
    LL id1,id2;
    LL size;
    int val;
    inline void read() { scanf("%d%d"AUTO AUTO"%d",&tr1,&tr2,&id1,&id2,&val); id1++; id2++; }
}node[maxm];
#define tr1(x) node[x].tr1
#define tr2(x) node[x].tr2
#define id1(x) node[x].id1
#define id2(x) node[x].id2
#define size(x) node[x].size
#define val(x) node[x].val
struct Dis
{
    int tr;
    LL p1,p2; // point in the tree
    bool operator < (const Dis t) const
    {
        if(tr ^ t.tr) return tr < t.tr;
        if(p1 ^ t.p1) return p1 < t.p1;
        return p2 < t.p2;
    }
    Dis (const int _tr,const LL _p1,const LL _p2) { tr=_tr; p1=_p1; p2=_p2; }
};
struct Sigma
{
    int tr;
    LL p;
    bool operator < (const Sigma t) const
    {
        if(tr ^ t.tr) return tr < t.tr;
        return p < t.p;
    }
    Sigma (const int _tr,const LL _p) { tr=_tr; p=_p; }
};
map <Dis,int> dis;
map <Sigma,int> sigma;
int dfs(int tr,LL p1,LL p2) // distance in the same tree
{
    if(!tr) return 0;
    if(p1 == p2) return 0;
    Dis now = Dis(tr,p1,p2);
    if(dis.count(now)) return dis[now];
    if(p1 <= size(tr1(tr)))
        if(p2 <= size(tr1(tr))) dis[now] = dfs(tr1(tr),p1,p2);
        else dis[now] = ((LL)dfs(tr1(tr),p1,id1(tr)) + dfs(tr2(tr),p2-size(tr1(tr)),id2(tr)) + val(tr)) %mod;
    else
        if(p2 > size(tr1(tr))) dis[now] = dfs(tr2(tr),p1-size(tr1(tr)),p2-size(tr1(tr)));
        else dis[now] = ((LL)dfs(tr1(tr),p2,id1(tr)) + dfs(tr2(tr),p1-size(tr1(tr)),id2(tr)) + val(tr)) %mod; // the same is here , id2 is refering to the node in tree2
    return dis[now];
}
int solve(int tr,LL p) // sigma of distance in tree tr to the node of p
{
    if(!tr) return 0;
    Sigma now = Sigma(tr,p);
    if(sigma.count(now)) return sigma[now];
    if(p <= size(tr1(tr))) sigma[now] =  ((LL)solve(tr1(tr),p) + solve(tr2(tr),id2(tr)) + size(tr2(tr))%mod*((LL)dfs(tr1(tr),p,id1(tr)) + val(tr))%mod) %mod;
    else sigma[now] = ((LL)solve(tr2(tr),p-size(tr1(tr))) + solve(tr1(tr),id1(tr)) + size(tr1(tr))%mod*((LL)dfs(tr2(tr),p-size(tr1(tr)),id2(tr)) + val(tr))%mod) %mod; // id2(tr) is refered to the node in tr2! , already adjusted.
    return sigma[now];
}
int F[maxm];
int m;
int main()
{
    freopen("zi.in","r",stdin);
    freopen("zi.out","w",stdout);
    memset(node,0,sizeof(node));
    dis.clear(); sigma.clear();
    scanf("%d",&m);
    for(int i=1;i<=m;i++) node[i].read();
    size(0) = 1;
    for(int i=1;i<=m;i++) size(i) = size(tr1(i)) + size(tr2(i));
    for(int i=1;i<=m;i++)
    {
        int t1 = size(tr1(i))%mod*solve(tr2(i),id2(i))%mod;
        int t2 = size(tr2(i))%mod*solve(tr1(i),id1(i))%mod;
        int t3 = size(tr1(i))%mod*size(tr2(i))%mod*val(i)%mod;
        F[i] = ((LL)F[tr1(i)] + F[tr2(i)] + t1 + t2 + t3) %mod;
    }
    for(int i=1;i<=m;i++) printf("%d\n",F[i]);
    return 0;
}
原创文章 152 获赞 15 访问量 6万+

猜你喜欢

转载自blog.csdn.net/ourfutr2330/article/details/52818701