11.3多校联训

T1 逻辑表达式

Sol
先把操作序列读入下来,然后把数存下来跑。
若要求出原式子计算结果,那就是一个栈,遇到操作符号就弹栈即可。再维护两个栈分别表示让栈顶元素为01的最小花费,一直做到最后即可。
注意字符串数组大小要开四倍。
Code

#include<bits/stdc++.h>
using namespace std;
namespace io
{
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c&15),c=getchar();
        return f?x:-x;
    }
    inline void print(int x)
    {
        static int s[20],len;
        len=0;
        if(x<0)putchar('-'),x=-x;
        if(x==0)
        {
            putchar('0');return;
        }
        while(x)
        {
            s[++len]=x%10;
            x/=10;
        }
        for(int i=len;i;i--)putchar(s[i]+'0');
        return;
    }
}
using namespace io;
const int maxn=500010;
int T,n,a[maxn],len;
char s[maxn<<2];
int st[maxn],head;
int mdf[maxn][2];
int main()
{
    freopen("logic.in","r",stdin);
    freopen("logic.out","w",stdout);
    T=read();
    while(T--)
    {
        n=read();len=head=0;
        while(s[len]!='\n')s[++len]=getchar();
        len--;
        for(int i=1;i<=n;i++)a[i]=read();
        for(int i=1;i<=len;i++)
        {
            if(s[i]==' ')continue;
            if(s[i]=='x')
            {
                int now=0;i++;
                while(s[i]!=' ')
                {
                    now=now*10+s[i]-'0';i++;
                }
                st[++head]=a[now];
                mdf[head][1]=(a[now]==0);
                mdf[head][0]=(a[now]==1);
            }else if(s[i]=='&')
            {
                int g=mdf[head-1][1]+mdf[head][1];
                int p=min(mdf[head][0]+min(mdf[head-1][0],mdf[head-1][1]),mdf[head][1]+mdf[head-1][0]);
                st[head-1]=st[head-1]&st[head];head--;
                mdf[head][1]=g;mdf[head][0]=p;
            }else if(s[i]=='|')
            {
                int g=mdf[head-1][0]+mdf[head][0];
                int p=min(mdf[head][1]+min(mdf[head-1][0],mdf[head-1][1]),mdf[head][0]+mdf[head-1][1]);
                st[head-1]=st[head-1]|st[head];head--;
                mdf[head][0]=g;mdf[head][1]=p;
            }
        }
        print(mdf[head][st[head]^1]);putchar('\n');
    }
    return 0;
}

T2 瘟疫公司

Sol
先想80分做法:枚举所有点集判断是否联通,联通则求出\(w\)的值。然后记搜枚举子集求最值即可。时间复杂度\(O(m*2^n+3^n)\)
100分做法真感觉不好想:设\(dp[i][j]\)表示\(i\)状态下已经计算\(j\)个点的最小花费。那么对于\(j<size[i]\),枚举\(i\)中包含的一个非割点\(x\),那么有

\[dp[i][j]=\min_{x}\ dp[i-(1<<x)][j] \]

对于\(j=size[i]\),枚举\(x\)以及先前个数\(k\),那么有

\[dp[i][size[i]]=\min_{x,k}\ dp[i-(1<<x)][k]+(size[i]-k)*w[i] \]

边界值\(dp[1<<x][1]=0\),表示初始感染点。最终答案就是\(dp[(1<<n)-1][n]\)
Code

#include<bits/stdc++.h>
using namespace std;
namespace io
{
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=0;c=getchar();}
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c&15),c=getchar();
        return f?x:-x;
    }
    inline void print(int x)
    {
        static int s[20],len;
        len=0;
        if(x<0)putchar('-'),x=-x;
        if(x==0)
        {
            putchar('0');return;
        }
        while(x)
        {
            s[++len]=x%10;
            x/=10;
        }
        for(int i=len;i;i--)putchar(s[i]+'0');
        return;
    }
}
using namespace io;
const int maxn=110,inf=1000000000;
int n,m;
struct edg
{
    int from,to,v;
    bool operator<(const edg &x)const
    {
        return v>x.v;
    }
}b[maxn];
struct edge
{
    int from,to,v;
    bool operator<(const edge &x)const
    {
        return v<x.v;
    }
}a[maxn];//零标号!!! 
int f[(1<<20)+10],g[(1<<20)+10][21],s[(1<<20)+10];
inline int lowbit(int x){return x&(-x);}
int fa[maxn];
inline int findf(int x)
{
    if(fa[x]==x)return x;
    return fa[x]=findf(fa[x]);
}
inline void pre()
{
    for(int i=1;i<(1<<n);i++)
    {
        if(i==lowbit(i))
        {
            s[i]=1;f[i]=0;continue;
        }
        int k=i;
        while(k)
        {
            s[i]++;k-=lowbit(k);
        }
        for(int j=0;j<n;j++)fa[j]=j;
        int cnt=0;
        for(int j=1;j<=m;j++)
        {
            int x=a[j].from,y=a[j].to,v=a[j].v;
            if((i&(1<<x))==0||(i&(1<<y))==0)continue;
            x=findf(x);y=findf(y);
            if(x==y)continue;
            fa[x]=y;f[i]+=v;cnt++;
            if(cnt==s[i]-1)break;
        }
        if(cnt!=s[i]-1)
        {
            f[i]=-1;continue;
        }
        int now=0;cnt=0;
        for(int j=0;j<n;j++)fa[j]=j;
        for(int j=1;j<=m;j++)
        {
            int x=b[j].from,y=b[j].to,v=b[j].v;
            if((i&(1<<x))==0||(i&(1<<y))==0)continue;
            x=findf(x);y=findf(y);
            if(x==y)continue;
            fa[x]=y;now+=v;cnt++;
            if(cnt==s[i]-1)break;
        }
        f[i]^=now;
    }
    return;
}
int main()
{
    freopen("plague.in","r",stdin);
    freopen("plague.out","w",stdout);
    n=read();m=read();
    for(int i=1;i<=m;i++)
    {
        a[i].from=read()-1;a[i].to=read()-1;a[i].v=read();
        b[i]=(edg){a[i].from,a[i].to,a[i].v};
    }
    sort(a+1,a+m+1);sort(b+1,b+m+1);
    pre();
    memset(g,0x3f,sizeof(g));
    for(int i=0;i<n;i++)g[1<<i][1]=0;
    for(int i=1;i<(1<<n);i++)
    {
        for(int j=2;j<s[i];j++)
        {
            for(int x=i;x;x-=lowbit(x))
            {
                if(f[i-lowbit(x)]==-1)continue;
                g[i][j]=min(g[i][j],g[i-lowbit(x)][j]);
            }
        }
        for(int x=i;x;x-=lowbit(x))
        {
            if(f[i-lowbit(x)]==-1)continue;
            for(int k=1;k<s[i];k++)
            {
                g[i][s[i]]=min(g[i][s[i]],g[i-lowbit(x)][k]+(s[i]-k)*f[i]);
            }
        }
    }
    print(g[(1<<n)-1][n]);
    return 0;
}

T3 搞搞新意思

Sol
肯定首先要求出原来树的直径,两遍DFS基操求出长度以及路径。
显然一次修改肯定在直径上。那么新直径只可能是以下三种:修改边断开后两个子树各自的直径,以及原直径-原长+修改长。
符号化表达,就是\(D=\max (d[u],d[v],oldD-w_0+w)\)。当\(\max (d[u],d[v])-oldD+w_0>w\)的时候,最值取在\(\max (d[u],d[v])\)里面。而不等式左边是一个常数,可以预处理。这一部分可以通过把直径两端分别作为跟跑两遍树形DP求得\(d\),把处理出来的数列排序,二分查找\(w\)的位置,在该位置前面的答案就是\(\max\ oldD-w_0+w\),后面的答案就是\(\max(d[u],d[v])\),这两部分都可以再用一个前缀和预处理出来,两式求最大值就是答案。时间复杂度\(\mathcal{O}(n\log n)\)
Code
写了180行挂了。还没调出来。

T4 图的直径

Sol
我是真没想到8e8还能过,不然考场就写70分了。
首先设\(da[l][r],db[l][r]\)表示区间\([l,r]\)中a,b的距离。设\(dis[l][r]=min(da[l][r],db[l][r])\)
区间DP,设状态\(dp[l][r][0/1/2]\)表示联通了\(l,r\)的情况下,区间内任意一点到左/右/区间内另一点的最长长度。
二分答案\(x\),剩下的贺题解:

Code
我Sol都贺的,指望有代码???

posted @ 2021-11-03 22:03  wwlvv  阅读(47)  评论(0)    收藏  举报