比赛链接
我是弱智吧,统计答案时少统计了一列 − 25 p t s -25pts −25pts
我是弱智吧,一个错误的贪心把所有的样例全过了, − 75 p t s -75pts −75pts
出题人是弱智吧,卡对取模意义下 / 0 /0 /0,要扩域才能过 − 30 p t s -30pts −30pts
感觉有点妙的
没什么好讲的,直接状压即可
注意到出题人是 s b sb sb, n = 0 n=0 n=0 时数据范围不一样
#include
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;
}
如果我们把体力值化成函数图像可以发现,答案为最低点的相反数
考虑钦定子树的访问顺序,使最小的体力值最大
考虑相邻比较,如果前一棵子树的 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
#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;
}
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
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;
}
考虑图中的黑边为出了三元环之间的边以外的其他相隔 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
#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;
}