noip模拟33 hunter defence connect
Hunter
考场:
考场上完全没思路,写了一个状压还挂了
正解:
一号猎人的存活时间等于在他之前死的猎人的数量加一,所以只要计算出在一号死之前有多少猎人死的期望就可以了。对于每一个猎人,在一号之前死的概率为 \(\frac{w_{i}}{w_{1}+w_{i}}\)所以,他的期望便是枚举每一个人后的和加一
代码实现
#include<bits/stdc++.h>
#define lt long long
#define int long long
using namespace std;
const int N=1050000,mod=998244353;
int n;
lt f[N];
lt ch[N];
lt nn;
lt ens;
bool vis[N];
lt poow(lt x,lt y){
lt ans=1;
while(y){
if(y&1) ans=(ans*x)%mod;
y>>=1;
x=x*x%mod;
}
return ans;
}
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;++i){
scanf("%lld",&ch[i]);
}
ens=1;
for(int i=2;i<=n;++i){
ens=(ens+(ch[i]*poow(ch[1]+ch[i],mod-2))%mod)%mod;
}
printf("%lld",ens);
return 0;
}
Defence
考场上想到了一个奇怪的思路,不细说了
正解:
使用线段树合并维护每一个节点(本人第一次写线段树合并)。
首先,dfs,在向下dfs时,对于有法术的点动态开点建立线段树。递归到最底层,回溯,如果父节点有线段树则合并父节点与子节点,若没有则先将一个儿子赋给父亲
tip:线段树合并的时间复杂度是O(nlogn)
代码实现
#include<bits/stdc++.h>
using namespace std;
const int N=100050;
int n,m,q,tot,pnt;
int head[N],root[N];
int ans[N];
vector<int> zkk[N];
struct tree{
int le,ri,l,r,len;
}t[N*80];
struct edge{
int nxt,to;
}e[N*2];
void ade(int x,int y){
e[++tot].nxt=head[x];
e[tot].to=y;
head[x]=tot;
}
void pushup(int u){
if(t[u].l){
t[u].le=t[t[u].l].le;
t[u].ri=t[t[u].l].ri;
t[u].len=t[t[u].l].len;
}
else{
t[u].le=t[t[u].r].le;
}
if(t[u].r){
t[u].ri=t[t[u].r].ri;
t[u].len=max(t[u].len,t[t[u].r].len);
}
if(t[u].l&&t[u].r){
t[u].len=max(t[u].len,t[t[u].r].le-t[t[u].l].ri-1);
}
}
void adp(int &u,int l,int r,int x){
if(!u) u=++pnt;
if(l==r){
t[u].le=x;
t[u].ri=x;
return;
}
int mid=((l+r)>>1);
if(x<=mid){
adp(t[u].l,l,mid,x);
}
else{
adp(t[u].r,mid+1,r,x);
}
pushup(u);
}
void merge(int x,int y){
if(!t[x].l&&!t[x].r&&!t[y].l&&!t[y].r) return ;
if(t[x].l&&t[y].l) merge(t[x].l,t[y].l);
if(t[x].r&&t[y].r) merge(t[x].r,t[y].r);
if(!t[x].l) t[x].l=t[y].l;
if(!t[x].r) t[x].r=t[y].r;
pushup(x);
}
void dfs(int u,int z){
int size=zkk[u].size();
for(int i=0;i<size;++i){
adp(root[u],1,m,zkk[u][i]);
}
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==z) continue;
dfs(v,u);
if(root[u]){
merge(root[u],root[v]);
}
else{
root[u]=root[v];
}
}
// printf("%d %d %d %d %d\n",u,m-t[root[u]].ri+t[root[u]].le-1,t[root[u]].len,t[root[u]].le,t[root[u]].ri);
ans[u]=max(t[root[u]].len,m-t[root[u]].ri+t[root[u]].le-1);
if(!root[u]){
ans[u]=-1;
}
}
int main(){
scanf("%d%d%d",&n,&m,&q);
int uc,vc;
for(int i=1;i<n;++i){
scanf("%d%d",&uc,&vc);
ade(uc,vc);
ade(vc,uc);
}
int ac,bc;
for(int i=1;i<=q;i++){
scanf("%d%d",&ac,&bc);
zkk[ac].push_back(bc);
}
dfs(1,0);
for(int i=1;i<=n;++i){
// printf("%d:",root[i]);
printf("%d\n",ans[i]);
}
return 0;
}
Connect
考场上通过输出(n-2)骗了点分,状压真的不会写呀!!!
正解:
考虑状压
如果想让从起点到终点只有一条路径,需要将剩下的节点与点集只与路径上的一个点相连(此时可以保证从起点到终点的路径的唯一性)
考虑实现,建立dp数组f[i][j],i表示状态,j表示当前路径的终点。
枚举状态,枚举终点,进行两种转移
1.枚举一个点集t(t与状态点集不相交),考虑让t与终点连边的贡献,与t点集中的内部贡献,终点不变
2.枚举j所能到达的点,作为下一个终点进行转移
tip:枚举一个集合的子集的方法
for(int tt=ss;tt;tt=(tt-1)&ss)
tip2:可以先维护答案正确性(连接一条从起点到终点的链),再使不影响答案正确性的需要部分加入(加入剩余点集)
代码实现
#include<bits/stdc++.h>
#define lt long long
#define int long long
using namespace std;
int n,nn,m,tot,toc;
int head[20];
int f[40000][20];
int dis[40000][20];
int t[40000];
struct edge{
int fr,nxt,to,dis;
}e[7000],c[5000];
void ade(int x,int y,int z){
e[++tot].nxt=head[x];
e[tot].dis=z;
e[tot].to=y;
head[x]=tot;
}
void adc(int x,int y,int z){
c[++toc].fr=x;
c[toc].to=y;
c[toc].dis=z;
}
signed main(){
scanf("%lld%lld",&n,&m);
int uc,vc,wc;
for(int i=1;i<=m;i++){
scanf("%lld%lld%lld",&uc,&vc,&wc);
adc(uc,vc,wc);
ade(uc,vc,wc);
ade(vc,uc,wc);
}
nn=(1<<n)-1;
for(int i=1;i<=nn;++i){
for(int j=1;j<=m;++j){
if((i>>(c[j].fr-1)&1)&&(i>>(c[j].to-1)&1)){
t[i]+=c[j].dis;
}
}
// printf("%lld\n",t[i]);
}
for(int j=1;j<=n;j++){
for(int tt=nn;tt;tt=(tt-1)&nn){
int sum=0;
for(int k=head[j];k;k=e[k].nxt){
int v=e[k].to;
if(!((tt>>(v-1))&1)) continue;
sum+=e[k].dis;
}
dis[tt][j]=sum;
}
}
for(int i=1;i<=nn;++i){
for(int j=1;j<=n;++j){
f[i][j]=-0x3f3f3f3f;
}
}
f[1][1]=0;
for(int i=0;i<=nn;++i){
for(int j=1;j<=n;++j){
if(f[i][j]==-0x3f3f3f3f)continue;
if((i>>((j-1)&1))==0) continue;
for(int k=head[j];k;k=e[k].nxt){
int v=e[k].to;
if((i>>(v-1))&1) continue;
f[i|(1<<(v-1))][v]=max(f[i|(1<<(v-1))][v],f[i][j]+e[k].dis);
}
int ss=nn^i;
for(int tt=ss;tt;tt=(tt-1)&ss){
f[i|tt][j]=max(f[i|tt][j],f[i][j]+t[tt]+dis[tt][j]);
}
}
}
lt ens=0;
ens=t[nn]-f[nn][n];
printf("%lld\n",ens);
return 0;
}

浙公网安备 33010602011771号