noip模拟6(T2更新

由于蒟弱目前还没调出T1和T2,所以先写T3和T4。(T1T2更完辣! update in 6.12 07:19

T3 大佬


题目描述:

他发现katarina大佬真是太强了,于是就学习了一下katarina大佬的做题方法。
比如这是一本有n道题的练习册,katarina大佬每天都会做k道题。
第一天做第1~k题,第二天做第2 ~k+1 题……第n 天做第n-k+1 ~n 道题。
但是辣鸡 ljh 又不想太累,所以他想知道katarina大佬做完这本练习册的劳累度。
每道题有它的难度值,假设今天katarina大佬做的题目中最大难度为t,那么今天katarina大佬的劳累度就是wt?,做完这本书的劳累值就是每天的劳累值之和。
但是辣鸡ljh一道题都不会,自然也不知道题目有多难,他只知道题目的难度一定在1~m之间随机。
他想让即将参加 NOIP 的你帮他算算katarina大佬做完这本书的劳累值期望


看到是期望题,直接就跳了,回头看才知道这么水,没一会就想到正解了,奈何莫得时间了,淦。

\(f[i]\)表示这\(k\)道题中,\(i\)是最大难度的概率。

\(f1=(1/m)^k\),\(f2=(2/m)^k-(1/m)^k\),\(fm=1-((m-1)/m)^k\)

于是求得概率后,一天期望的贡献就是\(\sum_{i=1}^{m}w[i]*f[i]\).最后乘上总共的天数就是答案。

Code

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
namespace EMT
{
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;
}
#define F(i, a, b) for (register int i = a; i <= b; i++)
#define f(x) for (register int i = head[x], j; i; i = e[i].next)
#define pf printf
inline void pi(long long x)
{
    pf("%lld ", x);
}
inline void pn() { printf("\n"); }
inline void ps(int a[], int size)
{
    F(i, 1, size)
    pi(a[i]);
    pn();
}
#define int long long
const int mod=1000000007,N=510;
int ans,n,m,k;
inline int ksm(int a,int b){
    int ans=1;
    while(b){
        if(b&1)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }return ans;
}
int w,pre,one,now,inv;
inline signed main(){
    n=read();m=read();k=read();
    if(k>n){pi(0);return 0;}
    F(i,1,m){
        w=read();
        now=ksm(i%mod,k);
        one+=w*(now-pre+mod)%mod;
        one%=mod;
        pre=now;
    }pi(one*(n-k+1)%mod*ksm(ksm(m,k),mod-2)%mod);
}
} // namespace EMT
signed main() { return EMT::main(); }

逆元最后再运算&&ksm(b&1)!!!

T4 宝藏


题目描述:
图片.png


总觉得在哪里见过,原来是蓝书

但正解可忘得一干二净了,还是考场上想出的正解,结果转移时没转移全,痛失85pts

\(f[i][s]\)表示以\(i\)为根,状态\(s\)用位运算表示已统计过的点,其中\(i\)可以用滚动数组滚掉。

用辅助数组\(len[s][k]\)表示状态为\(s\)\(k\)距离根节点的距离,然后dp式子就显然了。

Code

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
namespace EMT
{
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;
}
#define F(i, a, b) for (register int i = a; i <= b; i++)
#define pf printf
inline void pi(int x)
{
    pf("%d ", x);
}
inline void px(int x)
{
    int ans[15]={0,0,0,0,0,0}, co = 0;
    while (x)
    {
        ans[++co] = x & 1;
        x >>= 1;
    }
    for (int i = 4; i >= 1; i--)
        pf("%d", ans[i]);
    pf(" ");
}
inline void pn() { printf("\n"); }
inline void ps(int a[], int size)
{
    F(i, 1, size)
    pi(a[i]);
    pn();
}
const int N = 14, maxn = 50000000;
int n, m, f[1 << N], w[N][N], a[N][N], len[1 << N][N];
signed main()
{
    //freopen("d.out","w",stdout);
    n = read();
    m = read();
    F(i, 1, n)
    F(j, 1, n)
    if (i != j)
        w[i][j] = maxn;
    F(i, 1, m)
    {
        int x = read(), y = read();
        if (x == y)
        {
            read();
            continue;
        }
        if (w[x][y] != maxn)
            w[y][x] = w[x][y] = min(w[x][y], read());
        else
            w[y][x] = w[x][y] = read(), a[x][++a[x][0]] = y, a[y][++a[y][0]] = x;
    }
    int ans = maxn;
    F(root, 1, n)
    {
        F(i,1,(1<<n)-1)f[i]=maxn;
        memset(len, 0, sizeof(len));
        f[1 << (root - 1)] = 0;
        len[1 << (root - 1)][root] = 1;
        F(i, 1, (1 << n) - 1)
        {
            //if(root==1)px(i),pi(f[i]),pn();
            if(f[i]!=maxn){
            F(j, 1, n)
            {
                if (i & (1 << (j - 1)))
                {
                    F(k, 1, a[j][0])
                    {
                        if (!(i & (1 << (a[j][k] - 1))))
                        {
                            if(f[i | (1 << (a[j][k] - 1))]>f[i] + w[j][a[j][k]] * len[i][j]){
                            F(l,1,n)len[i | (1 << (a[j][k] - 1))][l]=len[i][l];
                            len[i | (1 << (a[j][k] - 1))][a[j][k]] = len[i][j] + 1;
                            f[i | (1 << (a[j][k] - 1))] = f[i] + w[j][a[j][k]] * len[i][j];
                            }
                        }
                    }
                }
            }
            }
        }
        ans = min(ans, f[(1 << n) - 1]);
    }
    pi(ans);
    return 0;
}
} // namespace EMT
signed main() { return EMT::main(); }

T1 辣鸡

好具有嘲讽属性的名称。。。
题目描述:

辣鸡ljh NOI之后就退役了,然后就滚去学文化课了。

然而在上化学课的时候,数学和化学都不好的ljh却被一道简单题难住了,受到了大佬的嘲笑。

题目描述是这样的:

在一个二维平面上有一层水分子,请问形成了多少个氢键?

这个二维平面可以看做一个类似棋盘的东西,每个格子可以容纳一个水分子,左下角的格子为(0,0),这个格子右边的格子为(1,0),上方格子为(0,1),以此类推。

辣鸡ljh当然不会做了,所以他来求助JeremyGou,JeremyGou一眼就看穿了真相,并想用这道题来考一考正在做NOIP模拟赛的你。

注:在本题中,我们认为一个水分子能与和它曼哈顿距离为2且直线距离小于2的其他格子形成氢键。


简洁大法万岁!
将多个操作按x排序,\(l\),\(r\),为二者交集,\(ll\),\(rr\)为二者并集,
分情况讨论即可。

Code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
namespace EMT
{
#define int long long
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;
}
#define F(i, a, b) for (register int i = a; i <= b; i++)
#define f(x) for (register int i = head[x], j; i; i = e[i].next)
#define pf printf
inline void pi(long long x)
{
    pf("%lld ", x);
}
inline void pn() { printf("\n"); }
inline void ps(bool a[], int size)
{
    F(i, 1, size)
    pi(a[i]);
    pn();
}
const int N = 1e5 + 100;
int n, ans, maxl, maxh;
int maxx, maxy;
struct ques
{
    int x1, y1, x2, y2;
}q[N];
inline bool com(ques a, ques b)
{
    if (a.x1 == b.x1)return a.x2 < b.x2;
    else return a.x1<b.x1;
}
inline int size(int x,int y,int X,int Y){
    return (Y-y)*(X-x)*2;
}
inline void s(){pf("shit\n");}
inline void getans(int a,int b){
    int lx1=q[a].x1,rx1=q[a].x2,ly1=q[a].y1,ry1=q[a].y2;
    int lx2=q[b].x1,rx2=q[b].x2,ly2=q[b].y1,ry2=q[b].y2;
    if(ry1+1==ly2||ry2+1==ly1)
    {
        int l=max(lx1,lx2),ll=min(lx1,lx2);
        int rr=max(rx1,rx2),r=min(rx1,rx2);
        if(l>r+1)return;
        if(l==r+1){ans++;return;}
        if(l<=r)ans+=2*(r-l);
        if(l>ll)ans++;if(r<rr)ans++;
    }
    if(rx1+1==lx2)
    {
        int l=max(ly1,ly2),ll=min(ly1,ly2);
        int r=min(ry1,ry2),rr=max(ry1,ry2);
        if(l>r+1)return;
        if(l==r+1){ans++;return;}
        if(l<=r)ans+=2*(r-l);
        if(l>ll)ans++;if(r<rr)ans++;
    }
}
signed main()
{
    n = read();
    F(i, 1, n)q[i].x1=read()+1,q[i].y1=read()+1,q[i].x2=read()+1,q[i].y2=read()+1;
    F(i,1,n)ans+=size(q[i].x1,q[i].y1,q[i].x2,q[i].y2);
    sort(q+1,q+n+1,com);
    //pi(ans);s();s();pn();
    F(i,1,n)F(j,i+1,n)if(q[j].x1-q[i].x2>1)break;else getans(i,j);
    pi(ans);
    return 0;
}
} // namespace EMT
signed main() { return EMT::main(); }
/*
 10
0 10 0 10
0 8 0 9
0 0 7 0
0 2 9 2
0 3 10 7
1 8 8 9
4 10 8 10
8 0 9 1
9 8 10 8
10 0 10 2
 */

T2 模板


题目描述:

辣鸡ljh NOI之后就退役了,然后就滚去学文化课了。

他每天都被katarina大神虐,仗着自己学过一些姿势就给katarina大神出了一道题。

有一棵 \(n\) 个节点的以 1 号节点为根的树,每个节点上有一个小桶,节点\(u\)上的小桶可以容纳\(k_{u}\)个小球,ljh每次可以给一个节点到根路径上的所有节点的小桶内放一个小球,如果这个节点的小桶满了则不能放进这个节点,在放完所有小球之后就企图去***难katarina大神,让katarina大神回答每个节点的小桶内的小球有多少种颜色。

然而katarina大神一眼就秒掉了,还说这就是一道傻逼模板题。

现在katarina大神想考考即将参加NOIP2019的你能不能回答上辣鸡ljh的问题。

第一行,一个整数n,树上节点的数量。

接下来n ? 1行,每行两个整数u, v,表示在u, v之间有一条边。

接下来一行n个整数, ~ 表示每个节点上的小桶数量。

下一行是一个整数m,表示ljh进行的操作数量。

接下来m行,每行两个整数x, c,分别表示进行操作的节点和小球颜色。

下一行是一个整数Q,表示你需要回答的询问数。

接下来Q行,每行一个整数x,表示一个询问。


本来昨天就能A掉的,结果没考虑到负数的情况,快读直接跳过负号,

导致连WA n次的惨烈局面。(话说要是我不看测试点还要调多久啊。。。

教训:以后打快读不能偷懒为了卡一点小常数忽略负号了。

本题让我对\(splay\)的认识加深了许多。

首先是以修改时间为下标,修改时记录上每个点的时间,查询时查询时间区间就行了。

另外,对于每个点一开始都建一个\(splay\),其中包含\(root\)和一个\(map\)记录是否出现过这种小球。

当修改时先只修改最子叶的\(splay\),因为下面有着\(dfs\),可以将子树的状态合并到父节点上

至于合并的方法,是启发式合并,以前一直以为这是个什么特别厉害的东西,其实就是暴力把所有子树上的节点按照传统方式插入到父节点上...(父节点:size较大的点;
子结点:size小,插入简便的点——和\(splay\)上的父节点、子结点区分开来,这么说来,其实线段树也可以这么做吧

于是我们就可以在\(dfs\)的时候预处理出每个结点的答案,查询时直接输出即可。

\(splay\)\(map\)的结合应用\(get\sqrt{}\)

还有,对于每个结点有一个\(rec\)数组,相当于记录了该节点在哪一颗\(splay\)树上,
在合并时如果合并到子树上就更改父亲的\(rec\),而合并到父亲上就不用更改了,因为子树的答案已经统计完了,修改\(rec\)只会浪费一点时间。

\(splay\)的中序遍历\(get\sqrt{}\)(因为左子树时间小,右子树时间大,父节点时间位于二者之间,所以按照左—父—右的顺序插入。

到这里蒟弱的思路就发表完毕了,下面是code:

Code

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
namespace EMT
{
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    const int N=1e5+100;
    inline 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;}
    struct node{
        int son[2],tim,size,cnt,val,fa,col;
    }t[N<<2];
    int head[N],co,num,ans1,rec[N],n,m,q,now;
    struct edge{int next,to;}e[N<<1];
    inline void add(int next,int to){e[++co].next=head[next],e[co].to=to,head[next]=co;}
    struct tree{
        map<int,int>mp;
        int root;
        inline int siz(){return t[root].size;}
        inline int get(int x){return t[t[x].fa].son[1]==x;}
        inline void up(int x){t[x].size=t[t[x].son[1]].size+t[t[x].son[0]].size+1;t[x].cnt=t[t[x].son[1]].cnt+t[t[x].son[0]].cnt+t[x].val;}
        inline void res(int x){
            int old=t[x].fa,oldf=t[old].fa,k=get(x);
            t[old].son[k]=t[x].son[k^1];
            t[t[x].son[k^1]].fa=old;
            t[x].son[k^1]=old;t[old].fa=x;
            if(oldf)t[oldf].son[t[oldf].son[1]==old]=x;
            t[x].fa=oldf;
            up(old);up(x);
        }
        inline void splay(int x){
            for(register int fa;(fa=t[x].fa);res(x))
                if(t[fa].fa)res(get(fa)==get(x)?fa:x);
            root=x;
        }
        inline void insert(int tim,int col,int val){
            int f=0,x=root;bool bg;
            while((!bg)||(x&&t[x].tim!=tim))bg=1,f=x,x=t[x].son[tim>t[x].tim];
            x=++num;
            if(f)t[f].son[tim>t[f].tim]=x;
            t[x].tim=tim;t[x].val=t[x].cnt=val;t[x].col=col;t[x].fa=f;t[x].size=1;
            splay(x);
        }
        inline void change(int tim){
            int x=root;
            while(x&&t[x].tim!=tim)x=t[x].son[tim>t[x].tim];
            if(x)t[x].val=0;splay(x);
        }
        inline int find(int tim,int col){
            if(!mp[col]){
                mp[col]=tim;
                return 1;
            }
            else if(mp[col]>tim){
                change(mp[col]);
                mp[col]=tim;
                return 1;
            }
            else return 0;
        }
        inline int findx(int x,int lim){
            if(!x)return 0;
            if(t[t[x].son[0]].size>=lim)findx(t[x].son[0],lim);
            else if(t[t[x].son[0]].size+1>=lim)return t[t[x].son[0]].cnt+t[x].val+ans1;
            else ans1+=t[t[x].son[0]].cnt+t[x].val,findx(t[x].son[1],lim-t[t[x].son[0]].size-1);
        }
        inline int findval(int lim){
            ans1=0;
            if(!lim)return 0;
            if(lim>=t[root].size)return t[root].cnt;
            return findx(root,lim);
        }
    }a[N];
    inline void make(int x){
        if(!x)return;
        make(t[x].son[0]);
        a[now].insert(t[x].tim,t[x].col,a[now].find(t[x].tim,t[x].col));
        make(t[x].son[1]);
    }int ans[N],k[N];
    inline void dfs(int x,int fa){
        for(register int i=head[x],j;i;i=e[i].next){
            j=e[i].to;if(j==fa)continue;
            dfs(j,x);
            if(a[rec[x]].siz()<a[rec[j]].siz()){
                now=rec[j];
                make(a[rec[x]].root);
                rec[x]=now;
            }
            else{
                now=rec[x];
                make(a[rec[j]].root);
            }
        }
        ans[x]=a[rec[x]].findval(k[x]);
    }
    inline short main(){
        freopen("ac7.in","r",stdin);
        freopen("my.out","w",stdout);
        n=read();
        F(i,1,n-1){
            int x=read(),y=read();add(x,y);add(y,x);
        }
        F(i,1,n)k[i]=read(),rec[i]=i;
        m=read();
        F(i,1,m){
            int x=read(),y=read();
            a[rec[x]].insert(i,y,a[rec[x]].find(i,y));
        }
        dfs(1,0);
        q=read();
        while(q--){
            int x=read();
            printf("%d\n",ans[x]);
        }
        return 0;
    }
}
signed main() { return EMT::main();}
posted @ 2021-06-10 16:36  letitdown  阅读(71)  评论(1编辑  收藏  举报