8.8考試總結(NOIP模擬33)[Hunter·Defence·Connect]
無法逃避的是自我,而無法挽回的是過去。
前言
還算可以,不過 T1 少 \(\bmod\) 了一下掛了 25pts,T2 沒看清題面掛了 27pts。
下回注意吧。。
T1 Hunter
解題思路
感覺正解不是很好想到,但是看題解就比較好看懂。。
1 號獵人死亡的輪數等於在 1 號之前死亡的獵人數 +1。
根據期望的 線性 性, 就等於每個獵人比 1 號獵人先死的概率和。
不難發現第 i 個獵人比 1 號獵人先死的概率是 \(\dfrac{W_{i}}{W_{1}+W_{i}}\)
上面的內容直接從官方題解摘過來了,實現沒有任何難度。。
說一下記憶化搜尋吧。。。
很明顯 對於 \(n\le 10\)
對於 \(n\le 20\)的就需要記憶化了。
記憶化向下搜尋的時候最好不要傳一些概率那一類的東西,比較難把控。
對於下一層的概率最好在這一層就算好。。。
然後再特判一下 1 的情況就好了。
code
25pts DFS
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"Pass"<<endl using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch>'9'||ch<'0') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=1e5+10,M=1e6+10,mod=998244353; int n,ans,inv[M],s[N],f[1<<21]; int ksm(int x,int y) { int tmp=1; while(y) { if(y&1) tmp=tmp*x%mod; x=x*x%mod; y>>=1; } return tmp%mod; } void dfs(int sta,int round,int E) { if(!(sta&1)) return ; ans=(ans+f[sta]*s[1]%mod*round%mod*E%mod)%mod; for(int i=1;i<=n;i++) if((sta>>i-1)&1) dfs(sta^(1<<i-1),round+1,E*f[sta]%mod*s[i]%mod); } signed main() { n=read(); for(int i=1;i<=n;i++) s[i]=read(); for(int i=1;i<(1<<n);i++) { int sum=0; for(int j=1;j<=n;j++) if((i>>j-1)&1) sum=(sum+s[j])%mod; f[i]=ksm(sum,mod-2); } dfs((1<<n)-1,1,1); printf("%lld",ans); return 0; }
45pts 記憶化 DFS
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"Pass"<<endl using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while(ch>'9'||ch<'0') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=1e5+10,M=1e6+10,mod=998244353; int n,ans,inv[M],s[N],f[1<<21],dp[1<<21]; int ksm(int x,int y) { int tmp=1; while(y) { if(y&1) tmp=tmp*x%mod; x=x*x%mod; y>>=1; } return tmp%mod; } int dfs(int sta,int round) { if(!(sta&1)) return 0; if(dp[sta]) return dp[sta]; dp[sta]=(dp[sta]+f[sta]*s[1]%mod*round%mod)%mod; for(int i=1;i<=n;i++) if((sta>>i-1)&1) dp[sta]=(dp[sta]+dfs(sta^(1<<i-1),round+1)*f[sta]%mod*s[i]%mod)%mod; return dp[sta]; } signed main() { n=read(); for(int i=1;i<=n;i++) s[i]=read(); for(int i=1;i<(1<<n);i++) { int sum=0; for(int j=1;j<=n;j++) if((i>>j-1)&1) sum=(sum+s[j])%mod; f[i]=ksm(sum%mod,mod-2); } printf("%lld",dfs((1<<n)-1,1)); return 0; }
正解
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int mod=998244353,N=1e5+10;
int n,ans,s[N];
int ksm(int x,int y)
{
int tmp=1;
while(y)
{
if(y&1) tmp=tmp*x%mod;
x=x*x%mod;
y>>=1;
}
return tmp%mod;
}
signed main()
{
n=read();
for(int i=1;i<=n;i++)
s[i]=read();
for(int i=2;i<=n;i++)
ans=(ans+s[i]*ksm((s[i]+s[1]%mod),mod-2)%mod)%mod;
printf("%lld",(ans+1)%mod);
return 0;
}
T2 Defence
解題思路
首先感謝出題人沒有寫太多一個節點有多個法術的測試點。
其次,抱怨一下題目描述不清楚。。。
非常像之前做過的玫瑰花精這道題。
主要維護 7 個值,(其實有一些沒有用的,但也無傷大雅)。
最左邊的 1 的座標以及對應的最左側的連續的 0 的長度。
同樣的東西,在右邊也維護一個類似的。
還有一箇中間的最大區間的長度以及左右端點。
也就是下圖所表示的。
剩下的比較難寫的就是 push_up
函數了,別的還好。
然後就是線段樹合併一系列的事情了。。。。
code
AC程式碼
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
#define ls tre[x].l
#define rs tre[x].r
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=1e5+10;
int n,m,q,ans[N],root[N],fa[N];
int tot=1,head[N],nxt[N],ver[N];
int all;
vector<int > p[N];
struct Segment_Tree
{
int l,r,lp,rp,lmx,rmx,llen,rlen,len;
}tre[N*80];
void push_up(int x,int l,int r)
{
if(!ls&&!rs) return ;
if(ls)
{
tre[x].lp=tre[ls].lp;
tre[x].llen=tre[ls].lp-l;
}
else
{
tre[x].lp=tre[rs].lp;
tre[x].llen=tre[rs].lp-l;
}
if(rs)
{
tre[x].rp=tre[rs].rp;
tre[x].rlen=r-tre[rs].rp;
}
else
{
tre[x].rp=tre[ls].rp;
tre[x].rlen=r-tre[ls].rp;
}
int len1=tre[ls].len,len2=tre[rs].len,len3=0;
if(ls&&rs) len3=tre[ls].rlen+tre[rs].llen;
if(len1>=max(len2,len3))
{
tre[x].len=len1;
tre[x].lmx=tre[ls].lmx;
tre[x].rmx=tre[ls].rmx;
}
else if(len2>=max(len1,len3))
{
tre[x].len=len2;
tre[x].lmx=tre[rs].lmx;
tre[x].rmx=tre[rs].rmx;
}
else
{
tre[x].len=len3;
tre[x].lmx=tre[ls].rp;
tre[x].rmx=tre[rs].lp;
}
}
void insert(int &x,int l,int r,int pos)
{
if(!x) x=++all;
if(l==r)
{
tre[x].lp=tre[x].rp=tre[x].lmx=tre[x].rmx=l;
return ;
}
int mid=(l+r)>>1;
if(pos<=mid) insert(ls,l,mid,pos);
else insert(rs,mid+1,r,pos);
push_up(x,l,r);
}
int merge(int x,int y,int l,int r)
{
if(!x||!y) return x+y;
if(l==r) return x;
int mid=(l+r)>>1;
ls=merge(ls,tre[y].l,l,mid);
rs=merge(rs,tre[y].r,mid+1,r);
push_up(x,l,r);
return x;
}
void add(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs(int x)
{
if(p[x].size())
for(int i=0;i<p[x].size();i++)
insert(root[x],1,m,p[x][i]);
for(int i=head[x];i;i=nxt[i])
{
int to=ver[i];
dfs(to);
root[x]=merge(root[x],root[to],1,m);
}
ans[x]=max(tre[root[x]].len,tre[root[x]].llen+tre[root[x]].rlen);
}
signed main()
{
n=read();
m=read();
q=read();
for(int i=1,x,y;i<n;i++)
{
x=read();
y=read();
add(x,y);
fa[y]=x;
}
for(int i=1,x,y;i<=q;i++)
{
x=read();
y=read();
p[x].push_back(y);
}
dfs(1);
for(int i=1;i<=n;i++)
{
if(!root[i]) printf("-1\n");
else printf("%lld\n",ans[i]);
}
return 0;
}
造資料必備
可以出來鏈的,菊花圖的,滿二叉樹的。。
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
int random(int l,int r)
{
return rand()%(r-l+1)+l;
}
signed main()
{
freopen("date.in","w",stdout);
srand(time(0));
int n=10,m=20,q=random(1,10),opt=random(1,3);
cout<<n<<' '<<m<<' '<<q<<endl;
if(opt==1) for(int i=2;i<=n;i++) cout<<(i+1)/2<<' '<<i<<endl;
else if(opt==2) for(int i=2;i<=n;i++) cout<<1<<' '<<i<<endl;
else for(int i=2;i<=n;i++) cout<<i-1<<' '<<i<<endl;
for(int i=1;i<=q;i++) cout<<random(1,n)<<' '<<random(1,m)<<endl;
return 0;
}
T3 Connect
解題思路
考場上想的是先跑一個最大生成樹,然後對於 n 所在的子樹上進行一些操作。
後來儘管算了一下時間複雜度小的離譜,但還是交了上去QAQ。
然後考完之後看了一眼題解:狀壓,這複雜度才對嘛。
後來下午講題的時候 戰神 拿出了他的 AC_Code。
然後瞥了一眼四周都在截圖。。。(我也。。
\(f_{sta,i}\) 表示現在狀態是 sta ,這個聯通塊(或者說是掛了一些東西的鏈)的結尾是 j 節點。
然後狀態的轉移就可以是,在這個鏈上再掛上一些聯通塊,或者在鏈的尾部新加入節點。
預處理出每一種聯通塊內部的邊的總價值。
以及某一個聯通塊向聯通塊外面的某一個點的連邊的總價值。
最後進行狀壓轉移就好了。。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
const int N=16;
int n,m,cet[1<<N][N],edge[N][N],all[1<<N],f[1<<N][N];
signed main()
{
n=read();m=read();
for(int i=1,x,y,val;i<=m;i++)
{
x=read();y=read();val=read();
edge[x][y]=edge[y][x]=val;
}
for(int i=1;i<(1<<n);i++)
for(int j=1;j<=n;j++)
for(int k=j+1;k<=n;k++)
if((i>>j-1)&1 and (i>>k-1)&1)
all[i]+=edge[j][k];
for(int i=1;i<(1<<n);i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
if((i>>j-1)&1 and !((i>>k-1)&1))
cet[i][k]+=edge[j][k];
memset(f,-1,sizeof(f));
f[1][1]=0;
for(int i=1;i<(1<<n);i++)
for(int j=1;j<=n;j++)
if(f[i][j]!=-1)
{
for(int k=1;k<=n;k++)
if(!((i>>k-1)&1) and edge[j][k])
f[i|(1<<k-1)][k]=max(f[i|(1<<k-1)][k],f[i][j]+edge[j][k]);
for(int k=((1<<n)-1)^i;k;k=(((1<<n)-1)^i)&(k-1))
f[i|k][j]=max(f[i|k][j],f[i][j]+all[k]+cet[k][j]);
}
printf("%lld",all[(1<<n)-1]-f[(1<<n)-1][n]);
return 0;
}