11.3多校联训
T1 逻辑表达式
Sol
先把操作序列读入下来,然后把数存下来跑。
若要求出原式子计算结果,那就是一个栈,遇到操作符号就弹栈即可。再维护两个栈分别表示让栈顶元素为0或1的最小花费,一直做到最后即可。
注意字符串数组大小要开四倍。
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\),那么有
对于\(j=size[i]\),枚举\(x\)以及先前个数\(k\),那么有
边界值\(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都贺的,指望有代码???

浙公网安备 33010602011771号