2019 09 05

总结:合理的分配时间是关键 一道题不要刚太长的时间刚不出来 就快速的放下看其他的题目 最后有时间再写。

   信任自己在头脑清醒的时候打出的程序有的时候爆栈什么错误可能是编译器的锅。经典题目要多复习。

   注意根据部分分一步一步推出正解而不仅仅盲目打自己的暴力。

今天的模拟赛 题目质量一般 不过暴露了很多短板,这里我进行深刻的思考。

这道题不是关键 对于隔板法的证明 大多数都是这样的 在 n个位置放上m个数 且每个数之间相距距离至少为k 求方案数。

显然 我们把(m-1)*k 这些没有用的位置先抽出来 然后再在剩下没有限制的位置上随便放就好了 C(n-(m-1)*k,m);

正确性?我们直接观察很不容易观察出来这个式子的正确性甚至还有可能是不正确的感觉 ,不妨换一个非常直观的角度来看。从答案的角度进行分析,在此之前我们定义每一个板都插到我们选出的数字紧跟其后。这里分析答案的存在性。

对于一个合法的答案来说必然每两个数字之间是>=k的范围 如果刚好为k那么其实我们在前面那个组合数中只要两个数字选择在一起就好了,如果不为k 那么其实前面两个数字之间的距离不为1 遍观所有的答案都符合这样的特点所以该组合数是正确的。

这显然是正确的,我的证明非常的正确 接下来是判断基偶 注意有阶乘分解2的做法 n/2+n/2^2+n/2^3...这样的做法。

注意还有卢卡斯的做法 这个C(n,m) =C(n%2,m%2)*C(n/2,m/2)...注意 C(0,1)==0 显然易得n&m==m时为奇数。

#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &x) {
    x=0;
    T f=1,ch=getchar();
    while (!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}
int T,n,m,k;
int main() {
    read(T);
    while(T--) {
        read(n); read(m); read(k);
        if(k==1) {
            n-=2;m-=2;
            if((n&m)==m)puts("Yes");
            else puts("No");
            continue;
        } 
        else {
            n=n-1-k-k+1;m-=2;
            --k;n-=(m-1)*k;
            if((n&m)==m)puts("Yes");
            else puts("No");
            continue;    
        }
    }
    return 0;
}
View Code

第一眼 吧Cu提出来 然后暴力对于每次操作暴力dfs好了 其实不必要求枚举LCA那样还更不优秀。(当然你如果执意ST表LCA的话我也没什么办法。

正解:树形dp 先说一下我的非最优解吧,考虑到序列上 其实我们向左移动 左边+右边-线段树不断维护全局最小值就行了,当然对于序列上还有O(n)的做法。至于投射到树上我们显然可以发现其实是子树-外面子树+ 于是显然线段树维护dfs序就好了。

//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<cctype>
#include<utility>
#include<queue>
#include<stack>
#include<deque>
#include<vector>
#include<set>
#include<map>
#include<bitset>
#define db double
#define ll long long
#define INF 2000000000
#define min(x,y) ((x)>(y)?(y):(x))
#define l(p) s[p].l
#define r(p) s[p].r
#define sum(p) s[p].sum
#define tag(p) s[p].tag
#define zz p<<1
#define yy p<<1|1
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=200010;
int n,Q,len,ans,T,cnt,flag,last;
int Log[MAXN],dfn[MAXN],pos[MAXN],sz[MAXN];
int w[MAXN],dis[MAXN],f[MAXN][20],d[MAXN],an[MAXN];
int lin[MAXN],nex[MAXN<<1],ver[MAXN<<1],e[MAXN<<1];
struct wy{int x,id;}t[MAXN<<1];//问道玉门犹被遮 应将性命逐轻车
struct wx{int l,r,sum,tag;}s[MAXN<<2];//我本楚狂人 心向楚天歌
inline int cmp(wy a,wy b){return dfn[a.x]<dfn[b.x];}
int vis[MAXN];
inline void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
inline void build(int p,int l,int r)
{
    l(p)=l;r(p)=r;
    if(l==r){sum(p)=-w[pos[l]]+dis[pos[l]];return;}
    int mid=(l+r)>>1;
    build(zz,l,mid);
    build(yy,mid+1,r);
    sum(p)=min(sum(zz),sum(yy));
}
inline void swap(int &x,int &y){int tmp=x;x=y;y=tmp;}
inline void dfs(int x,int father)
{
    f[x][0]=father;d[x]=d[father]+1;
    dfn[x]=++cnt;pos[cnt]=x;sz[x]=1;
    for(int i=1;i<=T;++i)f[x][i]=f[f[x][i-1]][i-1];
    vis[x]=1;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn])continue;
        if(tn==father)continue;
        dis[tn]=dis[x]+e[i];
        dfs(tn,x);
        sz[x]+=sz[tn];
    }
}
inline int LCA(int x,int y)
{
    if(d[x]<d[y])swap(x,y);
    for(int i=Log[d[x]];i>=0;--i)
        if(d[f[x][i]]>=d[y])
            x=f[x][i];
    if(x==y)return x;
    for(int i=Log[d[x]];i>=0;--i)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    return f[x][0];
}
inline void pushdown(int p)
{
    tag(zz)+=tag(p);
    tag(yy)+=tag(p);
    sum(zz)+=tag(p);
    sum(yy)+=tag(p);
    tag(p)=0;return;
}
inline void modify(int p,int l,int r,int x)
{
    if(l<=l(p)&&r>=r(p))
    {
        last=sum(p);
        sum(p)=x;
        return;
    }
    int mid=(l(p)+r(p))>>1;
    if(tag(p))pushdown(p);
    if(l<=mid)modify(zz,l,r,x);
    if(r>mid)modify(yy,l,r,x);
    sum(p)=min(sum(zz),sum(yy));
}
inline void change(int p,int l,int r,int x)
{
    if(l>r)return;
    if(l<=l(p)&&r>=r(p))
    {
        sum(p)+=x;
        tag(p)+=x;
        return;
    }
    int mid=(l(p)+r(p))>>1;
    if(tag(p))pushdown(p);
    if(l<=mid)change(zz,l,r,x);
    if(r>mid)change(yy,l,r,x);
    sum(p)=min(sum(zz),sum(yy));
}
inline void solve(int x,int father)
{
    int mark=0;
    if(t[flag].x==x)
    {
        mark=1;
        modify(1,dfn[x],dfn[x],INF);
    }
    while(t[flag].x==x)
    {
        an[t[flag].id]=sum(1)+w[x];
        ++flag;
    }
    if(mark)modify(1,dfn[x],dfn[x],last);
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father)continue;
        change(1,dfn[tn],dfn[tn]+sz[tn]-1,-e[i]);
        change(1,1,dfn[tn]-1,e[i]);
        change(1,dfn[tn]+sz[tn],cnt,e[i]);
        solve(tn,x);
        change(1,dfn[tn],dfn[tn]+sz[tn]-1,e[i]);
        change(1,1,dfn[tn]-1,-e[i]);
        change(1,dfn[tn]+sz[tn],cnt,-e[i]);
    }
    return;
}
int main()
{
    //freopen("skylines.in","r",stdin);
    //freopen("skylines.out","w",stdout);
    n=read();
    for(int i=1;i<=n;++i)
    {
        w[i]=read();
        if(i!=1)Log[i]=Log[i>>1]+1;
    }
    T=Log[n];
    for(int i=1;i<n;++i)
    {
        int x,y,z;
        x=read();y=read();z=read();
        add(x,y,z);add(y,x,z);
    }
    Q=read();
    dfs(1,0);
    if(n<=2000&&Q<=2000)
    {
        for(int i=1;i<=Q;++i)
        {
            int x=read();ans=INF;
            for(int j=1;j<=n;++j)
            {
                if(x==j)continue;
                int lca=LCA(x,j);
                ans=min(ans,dis[x]-dis[lca]+dis[j]-dis[lca]-w[j]+w[x]);//小心爆int
            }
            printf("%d
",ans);
        }
        return 0;
    }
    else//离线 在树上跑
    {
        for(int i=1;i<=Q;++i)
        {
            int x=read();
            t[i]=(wy){x,i};
        }
        sort(t+1,t+1+Q,cmp);
        flag=1;build(1,1,cnt);solve(1,0);
        for(int i=1;i<=Q;++i)printf("%d
",an[i]);
    }
    return 0;
}
View Code

当然说一下考试的时候出现的情况 发现大样例爆栈了 一直以为是自己程序的锅 调了一小时才意识到编译器栈空间小了 一直在思考如何破解这个问题 bfs 栈模拟递归都几乎打了一遍然后无果因为我这个操作是很吃递归的,所以其他题没怎么看,综上要学会dev开栈。

开栈指令 工具->编译选项->-Wl,--stack=536870912 加上这一句话 这个开的应该是B 我们要开100MB 那就是100*1024*1024 所以左边其实是开了512MB。

树形dp还是刚才序列上的思路我们考虑一下到树上怎么做,显然的树形dp 我们先对x做一遍 然后得到答案之后换根即可。对每个点都得到了答案直接输出就好了,复杂度O(n);

突然发现这个树形dp是我以前写过的 不过当时没写成 因为我发现在换根的时候状态转移出自三个地方 自己的子树 父亲的子树 父亲的父亲之前的点的子树或者一些鬼畜的东西。

但是我们冷静的将其分开 合理的进行转移 就可以成功的写出 注意预处理的时候除了要取最大值还要取次大值为后续的换根做基础。

//#include<bits/stdc++.h>
#include<iostream>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<cctype>
#include<utility>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<deque>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<iomanip>
#include<stack>
#include<string>
#include<cstring>
#define INF 2000000000
#define ll long long
#define db double
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
#define mp(x,y) make_pair((x),(y))
using namespace std;
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 f<0?-x:x;
}
const int MAXN=200010;
int n,len,Q,wf;
int f[MAXN],fs[MAXN],w[MAXN],g[MAXN],s[MAXN];
//f[x] 表示以x这个子树的最小值 fs最小值的出自哪个儿子
//s[x] 表示次小值 w[x] 每个点的权值 g[x] 最终的答案 v[x] 父亲的贡献
int lin[MAXN],nex[MAXN<<1],ver[MAXN<<1],e[MAXN<<1];
inline void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
inline void dfs(int x,int father)
{
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father)continue;
        dfs(tn,x);
        int val=(f[tn]==wf?-w[tn]:min(-w[tn],f[tn]))+e[i];
        if(f[x]>val)
        {
            s[x]=f[x];
            f[x]=val;fs[x]=tn;
        }
        else if(s[x]>val)s[x]=val;
    }
}
inline void dp(int x,int father,int val)
{
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father)continue;
        if(fs[x]!=tn)
        {
            g[tn]=min(f[tn],min(val+e[i],f[x]+e[i]));
            dp(tn,x,min(val+e[i],min(f[x]+e[i],-w[tn])));
        }
        else 
        {
            g[tn]=min(f[tn],min(val+e[i],s[x]+e[i]));
            dp(tn,x,min(val+e[i],min(s[x]+e[i],-w[tn])));
        }
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<=n;++i)w[i]=read();
    memset(f,0x3f,sizeof(f));wf=f[0];
    memset(s,0x3f,sizeof(s));
    for(int i=1;i<n;++i)
    {
        int x,y,z;
        x=read();y=read();z=read();
        add(x,y,z);add(y,x,z);
    }
    dfs(1,0);//printf("%d
",f[1]);
    g[1]=f[1];
    dp(1,0,-w[1]);//直接换根
    Q=read();
    for(int i=1;i<=Q;++i)
    {
        int x=read();
        printf("%d
",w[x]+g[x]);
    }
    return 0;
}
View Code

此题 我觉得出的不太好 尽管我是被T2给坑的但是仍觉得此题不太好...题目让人度不懂ai是必要的 但是am+1之后的ai都是没有用的东西因为我们是不可能打到这一关的...

题目的具体意思是指初始的时候我们的可进关卡的集合大小为1 且里面的元素为1,第一关。每次我们都从这个集合中等概率的选出一个元素我们将要打的关卡是选出关卡+1的这关 打完之后可进关卡的集合加入这个元素。

问 这m次战斗 期望的ai和是多少。。。 我们发现每一次我们如果选的不是i这一关我们关卡的集合大小是不会变大的。越说越不清晰...

直接做吧 爆搜?好像是可行的 直接上期望吧. f[i][j]表示第i次进入关卡j的概率。然后答案就是每次战斗期望和。

//#include<bits/stdc++.h>
#include<iostream>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<cctype>
#include<utility>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<deque>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<iomanip>
#include<stack>
#include<string>
#include<cstring>
#define INF 1000000000
#define ll long long
#define db double
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
#define mp(x,y) make_pair((x),(y))
#define mod 998244353
using namespace std;
inline 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 f<0?-x:x;
}
const ll MAXN=25,maxn=100010;
ll a[maxn];
ll n,m;
ll w[MAXN],s[MAXN];
ll f[MAXN][220];//f[i][j]表示第i次选的数为j的概率
inline ll fast_pow(ll b,ll p)
{
    ll ans=1;
    while(p)
    {
        if(p&1)ans=ans*b%mod;
        p=p>>1;
        b=b*b%mod;
    }
    return ans;
}
int main()
{
    freopen("1.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<=n;++i)a[i]=read();
    f[1][0]=1;
    for(ll i=1;i<=m+1;++i)w[i]=fast_pow(i,mod-2);
    for(ll i=2;i<=m+1;++i)
    {
        for(ll j=1;j<i;++j)
        {
            for(ll k=1;k<i;++k)
                f[i][j]=(f[i][j]+f[k][j-1]*w[i-1])%mod;
            s[i]=(s[i]+f[i][j]*a[j])%mod;
        }
    }
    ll ans=0;
    for(ll i=2;i<=m+1;++i)ans=(ans+s[i])%mod;
    printf("%lld
",ans);
    return 0;
}
View Code

关于 这个分数的问题 我们发现 如果答案是一个小数精度可能不高 如果让求上述的同余方程的话 我们还得记录每一步的分母和分子有的时候甚至还要进行两个分数相加 这就又跟LCM有关了 可能爆long long了什么的非常难受,而且最后还得把所有的分数和起来 然后解一个同余方程 exgcd解这是非常的麻烦的。这里我们采用其他的做法来求出答案 x=a/b%mod ?就上面的同余式两边同乘b^(-1) x同余a/b%mod; 注意这里是乘法我们求的逆元 而取模先乘再模和先模再乘一样 得到x=a*b-1(%p);

这样就成功的快速的求出x了...ksm求逆元就好了。

原文地址:https://www.cnblogs.com/chdy/p/11482187.html