连着两次考试小于机房平均分了/kk,不努点力看来是不行了。
T1
将图分成一个团和一个独立集的方案数,正解是爆搜/fad。
然而把图建出来反而不好搜,于是不建图,只枚举每个点在团中还是在独立集中。这样看起来是 \(O(2^N)\) ,但实际上可以剪枝剪掉大部分不合法方案。
考场上时间没分配好,打了2h暴力无果。
考后bitset爆艹标算
\(\text{Code}:\)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <bitset>
using namespace std;
typedef long long lxl;
const int maxn=1e3+5,maxm=maxn*maxn;
const lxl mod=1000003;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
template <typename T>
inline void read(T &x)
{
x=0;T f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
x*=f;
}
int n,m;
bool vis[maxn];
bitset<maxn> bes[maxn],worker,part;
int ans;
inline void dfs(int x)
{
if(x==n+1) return ans+=(worker.any()&&part.any()),void();
if((worker|bes[x])==bes[x])
{
worker.set(x);
dfs(x+1);
worker.reset(x);
}
if(!(part&bes[x]).any())
{
part.set(x);
dfs(x+1);
part.reset(x);
}
}
int main()
{
// freopen("party.in","r",stdin);
// freopen("party.out","w",stdout);
int T;read(T);
while(T--)
{
read(n),read(m);
for(int i=1;i<=n;++i) bes[i].reset();
worker.reset();part.reset();
for(int i=1,u,v;i<=m;++i)
{
read(u),read(v);
bes[u].set(v);
bes[v].set(u);
}
ans=0;
dfs(1);
printf("%d\n",ans);
}
return 0;
}
T2
枚举小N选取的前缀,发现小A一定会选取最大后缀,那么直接上线段树就好了。
但是线段树用在这道题上是多余的,正解复杂度是 \(O(n)\)。
T1打了2h暴力无果后,开了T2,花了不到1h过了T2。
\(\text{Code}:\)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <climits>
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;
const int maxn=1e6+5;
template <typename T>
inline void read(T &x)
{
x=0;T f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
x*=f;
}
int n;
lxl A,B,a[maxn];
struct node
{
int l,r;
lxl rmax,sum;
node(int l,int r,lxl rmax,lxl sum)
:l(l),r(r),rmax(rmax),sum(sum){}
node(){}
inline node operator + (const node &T)const
{
return node(l,T.r,max(T.rmax,rmax+T.sum),sum+T.sum);
}
};
struct Segment_Tree
{
node tree[maxn<<2];
#define ls (p<<1)
#define rs (p<<1|1)
inline void init(int p,lxl d)
{
tree[p].sum=d;
tree[p].rmax=max(d,0ll);
}
void build(int p,int l,int r)
{
tree[p]=node(l,r,0,0);
if(l==r) return init(p,a[l]),void();
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
tree[p]=tree[ls]+tree[rs];
}
void modify(int p,int ps,lxl d)
{
int l=tree[p].l,r=tree[p].r;
if(l==r) return init(p,d),void();
int mid=(l+r)>>1;
if(ps<=mid) modify(ls,ps,d);
else modify(rs,ps,d);
tree[p]=tree[ls]+tree[rs];
}
}st;
int main()
{
// freopen("game.in","r",stdin);
// freopen("game.out","w",stdout);
read(n),read(A),read(B);
for(int i=1;i<=n;++i)
read(a[i]);
st.build(1,1,n);
lxl ans=LLONG_MIN;
for(int i=0;i<=n;++i)
{
if(i>=1) st.modify(1,i,a[i]*=-A);
node res=st.tree[1];
ans=max(ans,res.sum-res.rmax-res.rmax*B);
// res=st.tree[1];
}
printf("%lld\n",ans);
return 0;
}
T3
第一轮的困扰值可以直接放在边上。
第二轮也可以放在边上。考虑边 \(u\leftrightarrow v\) 对它两端点的影响:
- 若先在 \(u\) 上放一个居民,再在 \(v\) 上放一个居民,则困扰值为 \(F_u\times T_v+F_v\times (T_u+1)\) 。
- 若先在 \(v\) 上放一个居民,再在 \(u\) 上放一个居民,则困扰值为 \(F_v\times T_u+F_u\times (T_v+1)\) 。
将两式相减可以发现:先在 \(F\) 值大的城市上放居民的困扰值较小。于是可以贪心地放居民,由于决策只与 \(F_u\) 和 \(F_v\) 的大小有关,不难发现,最优的方案是先将 \(F\) 值大的城市放满居民,再放另一个城市。若 \(F_u>F_v\) ,则这条边造成的困扰值为:
\[F_u\times(U_u-T_u)\times T_v+F_v\times U_u\times (U_v-T_v) \]
否则 \(F_u<F_v\) 同理。
建出图跑最小生成树即可。
考试最后30min才开T3,所以没怎么思考,不过题解的这种转化思想是ao的,还是有收获。
\(\text{Code}:\)
#include <cstring>
#include <cstdio>
#include <algorithm>
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;
const int maxn=55;
template <typename T>
inline void read(T &x)
{
x=0;T f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
x*=f;
}
struct edge
{
int u,v;
lxl w;
edge(int u,int v,lxl w):u(u),v(v),w(w){}
edge(){}
inline bool operator < (const edge &T)const
{
return w<T.w;
}
}e[maxn*maxn];
int ecnt;
int n,fa[maxn];
lxl T[maxn],U[maxn],F[maxn],C,ans;
char G[maxn][maxn];
inline int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);}
inline lxl calcu(int i,int j)
{
if(F[i]<F[j]) swap(i,j);
return F[i]*(U[i]-T[i])*T[j]+F[j]*U[i]*(U[j]-T[j]);
}
inline bool merge(int u,int v)
{
int x=find(u),y=find(v);
if(x==y) return false;
fa[x]=y;
return true;
}
int main()
{
// freopen("reconstruction.in","r",stdin);
// freopen("reconstruction.out","w",stdout);
read(n);
for(int i=1;i<=n;++i) read(T[i]);
for(int i=1;i<=n;++i) read(U[i]);
for(int i=1;i<=n;++i) read(F[i]);
for(int i=1;i<=n;++i)
scanf(" %s\n",G[i]+1);
for(int i=1;i<=n;++i)
fa[i]=i,ans+=(U[i]-T[i])*(T[i]+U[i]-1)/2ll*F[i];
read(C);
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j)
if(G[i][j]=='Y')
{
ans+=calcu(i,j);
merge(i,j);
}
else e[++ecnt]=edge(i,j,calcu(i,j)+(T[i]+T[j])*C);
sort(e+1,e+ecnt+1);
for(int i=1;i<=ecnt;++i)
{
int u=e[i].u,v=e[i].v;
ans+=e[i].w*merge(u,v);
}
printf("%lld\n",ans);
return 0;
}