反思
A
我是弱智吧,统计答案时少统计了一列 − 25 p t s -25pts −25pts
B
我是弱智吧,一个错误的贪心把所有的样例全过了, − 75 p t s -75pts −75pts
C
出题人是弱智吧,卡对取模意义下 / 0 /0 /0,要扩域才能过 − 30 p t s -30pts −30pts
D
感觉有点妙的
题解
A
没什么好讲的,直接状压即可
注意到出题人是
s
b
sb
sb,
n
=
0
n=0
n=0 时数据范围不一样
#include <bits/stdc++.h>
using namespace std;
int n,m,k,x[20],y[20],c[500100];
double f[110][110][1<<10],res[1<<10];
const double eps=1e-8,inf=1000000000000000;
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
int main(){
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
n=read(),m=read(),k=read();
for(int i=0;i<n;i++) x[i]=read();
for(int i=1;i<=m;i++) y[i]=read();
for(int i=1;i<k;i++) c[i]=read();
if(!n){
double ans=0;
int las=0;
for(int i=1;i<=k;i++){
int a=read(),s=read(),z=read();
int tc=0;
if(las) tc=c[i-las];
double ty=1.0*y[s]*(1-1.0*tc/100.0);
double sc=1-max(0.0,1-ty/z)*max(0.0,1-ty/z);
ans+=sc*a;
if(sc+eps<0.64) las=i;
}
printf("%.2lf\n",ans);exit(0);
}
for(int S=0;S<1<<n;S++){
double cur=1;
for(int i=0;i<n;i++) if(S>>i&1) cur=cur*(1+1.0*x[i]/100.0);
res[S]=cur;
}
for(int i=0;i<=k;i++) for(int j=0;j<=k;j++) for(int S=0;S<1<<n;S++) f[i][j][S]=-inf;
f[0][0][0]=0;
for(int i=1;i<=k;i++){
int a=read(),s=read(),z=read();
for(int j=0;j<i;j++){
int tc=0;
if(j) tc=c[i-j];
for(int S=0;S<1<<n;S++)
for(int T=S;;T=(T-1)&S){
double ty=1.0*y[s]*(1-1.0*tc/100.0)*res[T];
double sc=1-max(0.0,1-ty/z)*max(0.0,1-ty/z);
if(sc+eps<0.64) f[i][i][S]=max(f[i][i][S],f[i-1][j][S^T]+sc*a);
else f[i][j][S]=max(f[i][j][S],f[i-1][j][S^T]+sc*a);
if(!T) break;
}
}
}
double ans=0;
for(int i=0;i<=k;i++) for(int S=0;S<1<<n;S++) ans=max(ans,f[k][i][S]);
printf("%.2lf\n",ans);
fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));
return 0;
}
B
如果我们把体力值化成函数图像可以发现,答案为最低点的相反数
考虑钦定子树的访问顺序,使最小的体力值最大
考虑相邻比较,如果前一棵子树的
s
u
m
a
−
2
s
u
m
w
sum_a-2sum_w
suma−2sumw 为
x
x
x,
min
{
f
v
−
w
i
,
s
u
m
u
−
2
w
i
}
\min\{f_v-w_i,sum_u-2w_i\}
min{fv−wi,sumu−2wi} 为
y
y
y,
w
i
w_i
wi 为连向子树的边的长度
考虑前一个比后一个更优即为:
min
{
y
1
,
x
1
+
y
2
}
>
min
{
y
2
,
x
2
+
y
1
}
\min\{y_1,x_1+y_2\}>\min\{y_2,x_2+y_1\}
min{y1,x1+y2}>min{y2,x2+y1}
考虑对
x
x
x 的情况分类讨论:
如果
x
1
,
x
2
x_1,x_2
x1,x2 正负性不同,那么
x
x
x 为正的排在前面
如果
x
1
,
x
2
<
0
x_1,x_2<0
x1,x2<0,那么
x
1
+
y
2
>
x
2
+
y
1
x_1+y_2>x_2+y_1
x1+y2>x2+y1 时才能把
1
1
1 排在前面
如果
x
1
,
x
2
>
0
x_1,x_2>0
x1,x2>0,那么
y
1
>
y
2
y_1>y_2
y1>y2 时才能把
1
1
1 排在前面
所以时间复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
#include <bits/stdc++.h>
#define int long long
#define fi first
#define sn second
using namespace std;
const int N=100100;
typedef pair<int,int> pii;
int n,a[N],f[N],totv[N];
int e[N<<1],w[N<<1],ne[N<<1],h[N],idx;
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
bool cmp(const pii &x,const pii &y){
if(x.fi<0&&y.fi>0) return false;
if(x.fi>0&&y.fi<0) return true;
if(x.fi<0) return x.fi+y.sn>x.sn+y.fi;
return x.sn>y.sn;
}
void dfs(int u,int fa){
vector<pii> vec;
totv[u]=a[u];
for(int i=h[u];~i;i=ne[i]){
int v=e[i];if(v==fa) continue;
dfs(v,u),totv[u]+=totv[v]-2*w[i];
vec.push_back({totv[v]-2*w[i],min(f[v]-w[i],totv[v]-2*w[i])});
}
sort(vec.begin(),vec.end(),cmp);
int cur=a[u];f[u]=0;
for(pii t:vec) f[u]=min(f[u],cur+t.second),cur+=t.first;
}
void add(int x,int y,int z){ e[idx]=y,w[idx]=z,ne[idx]=h[x],h[x]=idx++;}
signed main(){
freopen("horse.in","r",stdin);
freopen("horse.out","w",stdout);
n=read();
for(int i=1;i<=n;i++) a[i]=read();
memset(h,-1,sizeof(h));
for(int i=1;i<n;i++){
int x=read(),y=read(),z=read();
add(x,y,z),add(y,x,z);
}
dfs(1,-1);
printf("%lld\n",-f[1]);
fprintf(stderr,"%d ms\n",int64_t(1e3*clock()/CLOCKS_PER_SEC));
return 0;
}
C
O
(
n
3
)
O(n^3)
O(n3) 的树形
d
p
dp
dp 是简单的
这里有一个
t
r
i
c
k
trick
trick 是
p
x
×
(
选
x
个数的乘积
)
×
(
其他数不选的乘积
)
p^x\times(选x个数的乘积)\times(其他数不选的乘积)
px×(选x个数的乘积)×(其他数不选的乘积),可以把
p
x
p_x
px 放在乘积里面,然后就可以优化掉一个
n
n
n 的复杂度
然后换根即可
注意到出题人故意造了
∗
0
/
0
*0\;/0
∗0/0 的数据,所以要扩域,把数表示成
a
∗
0
b
a*0^b
a∗0b 的形式
时间复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1000100,P=998244353;
struct extend{ int a,b;};
int n,p,siz[N];
int inv[N],f[N];
extend ans[N];
int e[N<<1],ne[N<<1],h[N],idx;
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
void add(int x,int y){ e[idx]=y,ne[idx]=h[x],h[x]=idx++;}
int qmi(int a,int b){
int res=1;
for(;b;b>>=1){
if(b&1) res=1ll*res*a%P;
a=1ll*a*a%P;
}
return res;
}
extend operator *(extend x,int y){
if(!y) x.b++;
else x.a=1ll*x.a*y%P;
return x;
}
extend operator /(extend x,int y){
if(!y) x.b--;
else x.a=1ll*x.a*qmi(y,P-2)%P;
return x;
}
int ask(int sz){ return (1ll*p*inv[sz]+1-inv[sz])%P;}
void dfs(int u,int fa){
siz[u]=1;
for(int i=h[u];~i;i=ne[i]) if(e[i]!=fa) dfs(e[i],u),siz[u]+=siz[e[i]];
f[u]=ask(siz[u]),ans[1]=ans[1]*f[u];
}
void dfs2(int u,int fa){
for(int i=h[u];~i;i=ne[i]){
int v=e[i];if(v==fa) continue;
ans[v]=ans[u]/f[v]*ask(n-siz[v]);
dfs2(v,u);
}
}
int main(){
freopen("treap.in","r",stdin);
freopen("treap.out","w",stdout);
n=read(),p=read();
inv[1]=1;
for(int i=2;i<=n;i++) inv[i]=1ll*(P-P/i)*inv[P%i]%P;
memset(h,-1,sizeof(h));
for(int i=1;i<n;i++){
int x=read(),y=read();
add(x,y),add(y,x);
}
ans[1]={1,0},dfs(1,-1),dfs2(1,-1);
int ANS=0;
for(int i=1;i<=n;i++) if(!ans[i].b) ANS=(ANS+ans[i].a)%P;
ANS=1ll*ANS*inv[n]%P;
printf("%d\n",(ANS+P)%P);
fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));
return 0;
}
D
考虑图中的黑边为出了三元环之间的边以外的其他相隔
2
2
2 的点之间的连边
考虑到一个结论是三元环的个数是
O
(
m
m
)
O(m\sqrt m)
O(mm) 的
所以我们其实是可以直接通过一些方法来判掉三元环的情况的
具体来说,我们可以用两个队列在存储从
a
a
a 边来的点和从
b
b
b 边来的点,这样可以省掉一个优先队列的
l
o
g
log
log
这样的话,每个点只会拓展一次
考虑边权为
a
a
a 的边是好做的,直接暴力时间复杂度就是对的
对于边权为
b
b
b 的边,我们考虑每次枚举邻点
v
v
v 和邻点的邻点
2
2
2
w
w
w,邻点
2
2
2表示这个点是被邻点访问到时需要访问的邻点的集合
如果
w
w
w 是
u
u
u的邻居的话,就跳过,但
w
w
w 需要保存在
v
v
v 的邻点
2
2
2 中,否则,就更新答案,且把
w
w
w 从邻点
2
2
2 中删去
时间复杂度
O
(
m
m
)
O(m\sqrt m)
O(mm)
很牛!!!
#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long LL;
typedef pair<LL,int> pli;
const int N=150100,M=600100;
int n,m,a,b;
bool vis[N],tag[N];
LL f[N];
vector<int> G[N],T[N];
queue<int> Qa,Qb;
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
int main(){
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
n=read(),m=read(),a=read(),b=read();
for(int i=1;i<=m;i++){
int x=read(),y=read();
G[x].pb(y),G[y].pb(x);
T[x].pb(y),T[y].pb(x);
}
memset(f,0x3f,sizeof(f));f[1]=0;
Qa.push(1);
while(!Qa.empty()||!Qb.empty()){
int u;
if(Qb.empty()||(!Qa.empty()&&f[Qa.front()]<f[Qb.front()])){ u=Qa.front();Qa.pop();}
else{ u=Qb.front();Qb.pop();}
if(vis[u]) continue;
vis[u]=true;
for(int v:G[u]){
tag[v]=true;
if(f[u]+a<f[v]) f[v]=f[u]+a,Qa.push(v);
}
for(int v:G[u]){
vector<int> tmp;tmp.clear();
for(int w:T[v]){
if(vis[w]) continue;
if(tag[w]) tmp.pb(w);
else if(f[u]+b<f[w]) f[w]=f[u]+b,Qb.push(w);
}
swap(T[v],tmp);
}
for(int v:G[u]) tag[v]=false;
}
for(int i=2;i<=n;i++) printf("%lld\n",f[i]);
fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));
return 0;
}