HNOI2018
感觉很神,反正我完全没想到
分开考虑每一位,对于每一位i计算一个二进制数b[i],
对于第i位,从后往前第j个数这一位是1,那么b[i]^=(1<<j)
对于操作,从后往前考虑每个数前面的符号,把&看成1,|看成0
把一个操作序列看成一个二进制数c
发现|0和&1等于无影响,一个操作序列的最后一个|1或者&0决定结果的值
那么对于第i位,要使这一位为1,必须满足c<b[i]
(这一位是1,则要选择一个b[i]中是1的位置取|,并将它之前的所有操作任意取,之后的操作有唯一取法,相当于将二进制位中某个1变成0,高位不变,低位任意取,最后得到c<b[i])
那么将b从大到小排序,对于每个询问,首先满足找到最靠左的0必须在1后面,然后最靠左的0的位置为pos,答案为b[pos-1]-b[pos]
要取模,一开始傻乎乎地在那里写高精...
1 //Achen
2 #include<algorithm>
3 #include<iostream>
4 #include<cstring>
5 #include<cstdlib>
6 #include<vector>
7 #include<cstdio>
8 #include<queue>
9 #include<cmath>
10 #define For(i,a,b) for(int i=(a);i<=(b);i++)
11 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
12 const int N=5007,mod=1e9+7;
13 typedef long long LL;
14 typedef double db;
15 using namespace std;
16 const LL mxup=(1LL<<58);
17 int n,m,q,sa[N],rak[N],L;
18 LL ans[N];
19 char s[1007][N],ss[N];
20
21 template<typename T>void read(T &x) {
22 char ch=getchar(); x=0; T f=1;
23 while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
24 if(ch=='-') f=-1,ch=getchar();
25 for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
26 }
27
28 bool cmp(const int &A,const int &B) {
29 Rep(i,n,1) {
30 if(s[i][A]!=s[i][B]) return s[i][A]>s[i][B];
31 } return 1;
32 }
33
34 void pre() {
35 For(i,0,m)
36 Rep(j,n,1) {
37 int x=i==0?1:s[j][i]-'0';
38 ans[i]=((ans[i]<<1)%mod+x)%mod;
39 }
40 ans[0]=(ans[0]+1)%mod;
41 }
42
43 //#define DEBUG
44 int main() {
45 #ifdef DEBUG
46 freopen("hunt.in","r",stdin);
47 freopen("hunt.out","w",stdout);
48 #endif
49 read(n); read(m); read(q);
50 For(i,1,n) scanf("%s",s[i]+1);
51 For(i,1,m) sa[i]=i;
52 sort(sa+1,sa+m+1,cmp);
53 For(i,1,m) rak[sa[i]]=i;
54 pre();
55 while(q--) {
56 int ll=0,rr=m+1;
57 scanf("%s",ss+1);
58 For(i,1,m) {
59 int x; x=ss[i]-'0';
60 if(x==1) ll=max(ll,rak[i]);
61 else if(!rr||rr>rak[i]) rr=rak[i];
62 }
63 if(rr<ll) puts("0");
64 else {
65 LL rs=(rr==m+1?ans[sa[rr-1]]:(ans[sa[rr-1]]-ans[sa[rr]]+mod)%mod);
66 printf("%lld\n",rs);
67 }
68 }
69 return 0;
70 }
71 /*
72 5 5 1
73 01110
74 11011
75 10000
76 01010
77 00100
78 00100
79 */
贪心.发现一定有一种最优答案是在某个起点待着不动一会,然后一路走下去
考虑如果答案是走了很多圈,最后一个取的点为x,那么从最后时刻从x往前走一圈,一路上所有东西肯定都是已经出现了的,把游戏倒过来,就是从x往前走,在每个物品消失前取到它,那么最优策略一定是从x一步不停地走下去,那么正过来也是一样.
把原序列倍长,答案就等于
$ans=min_{i=1}^n(max_{j=i}^{2*n}T[j]-(j-i)+n-1)$
$设A[i]=T[i]-i$
$ans=min_{i=1}^n(max_{j=i}^{2*n}A[j]+i)+n+1$
维护和楼房重建很像.
用线段树维护答案,线段树上分别维护A[i]的最大值,和在l~r这段区间中来算(即n=r,i=l~n),i在l~mid中的答案的最小值
支持询问qry(x,l,r,suf)表示i在l,r这段去就,A[i]和suf取max后的答案,那么修改时可以调用qry在两个log的时间里更新线段树里的值
注意询问时直接输出线段树中根的答案,而不是调用qry(suf会出问题),
//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<queue>
#include<cmath>
#include<set>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
const int N=200007;
typedef long long LL;
typedef double db;
using namespace std;
int n,m,p,T[N],A[N],ans;
template<typename T> void read(T &x) {
char ch=getchar(); x=0; T f=1;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
#define lc x<<1
#define rc ((x<<1)|1)
#define mid ((l+r)>>1)
int mx[N<<2],sg[N<<2];
int qry(int x,int l,int r,int suf) {
if(l==r) return max(suf,mx[x])+l;
if(mx[rc]>=suf) return min(sg[x],qry(rc,mid+1,r,suf));
else return min(qry(lc,l,mid,suf),suf+mid+1);
}
void update(int x,int l,int r) {
mx[x]=max(mx[lc],mx[rc]);
sg[x]=qry(lc,l,mid,mx[rc]);
}
void change(int x,int l,int r,int pos) {
if(l==r) {
mx[x]=A[l]; sg[x]=A[l]+l; return;
}
if(pos<=mid) change(lc,l,mid,pos);
else change(rc,mid+1,r,pos);
update(x,l,r);
}
void build(int x,int l,int r) {
if(l==r) { mx[x]=A[l]; sg[x]=A[l]+l; return; }
build(lc,l,mid); build(rc,mid+1,r);
update(x,l,r);
}
//#define DEBUG
int main() {
#ifdef DEBUG
freopen("circle.in","r",stdin);
freopen("circle.out","w",stdout);
#endif
read(n); read(m); read(p);
For(i,1,n) {
read(T[i]); A[i]=T[i]-i;
T[i+n]=T[i]; A[i+n]=T[i+n]-(i+n);
}
build(1,1,n*2);
ans=sg[1]+n-1;
printf("%d\n",ans);
For(i,1,m) {
int x,y;
read(x); read(y);
if(p) x^=ans,y^=ans;
T[x]=y; A[x]=T[x]-x;
change(1,1,n*2,x);
T[x+n]=y; A[x+n]=T[x+n]-(x+n);
change(1,1,n*2,x+n);
ans=sg[1]+n-1;
printf("%d\n",ans);
}
return 0;
}
如果给出的一棵树,可以直接树dp,时间复杂度O(n)
发现非树边很少,可以枚举非树边上的点的状态,然后dp,时间复杂度为2^d*n(只用枚举每条非树边中一个点的状态)
考虑优化这个算法,发现每次树dp只有非树边上的点有影响,重复计算了很多没有必要的过程
那么把非树边拿出来建虚树,每次在虚树上dp
先对原树dp一次求出在虚树上dp要用到的系数
设g[x][0/1]表示常数,即x的那些没有任何关键点的儿子的贡献.
k[x][0/1][0/1]表示我对于我下面(包括我)第一个关键点的转移系数
即(设我下面第一个关键点为w)
f[x][0]= k[x][0][0]*f[w][0]+k[x][0][1]*f[w][1];
f[x][1]= k[x][1][0]*f[w][0]+k[x][1][1]*f[w][1];
边界为若我为关键点,
k[x][0][0]=1,k[x][0][1]=0;
k[x][1][0]=0,k[x][1][1]=1;
dp出k和g,就可以在虚树上转移了
//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<queue>
#include<cmath>
#include<set>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
const int N=200007,p=998244353;
typedef long long LL;
typedef double db;
using namespace std;
int n,m,eu[N],ev[N],tot;
template<typename T> void read(T &x) {
char ch=getchar(); x=0; T f=1;
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=-1,ch=getchar();
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}
int ecnt,fir[N],nxt[N<<1],to[N<<1];
void add(int u,int v) {
nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u;
}
int sz[N],is[N],vis[N];
void dfs(int x,int fa) {
vis[x]=1;
for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa) {
if(!vis[to[i]]) {
dfs(to[i],x);
sz[x]+=sz[to[i]];
}
else {
is[x]=1;
if(to[i]<x) eu[++tot]=x,ev[tot]=to[i];
}
}
is[x]=(is[x]||sz[x]>=2);
sz[x]=(sz[x]||is[x]);
}
struct pt {
LL a,b;
pt(){}
pt(int a,int b):a(a),b(b){}
friend pt operator +(const pt&A,const pt&B) {
return pt((A.a+B.a)%p,(A.b+B.b)%p);
}
friend pt operator *(const pt&A,const LL&B) {
return pt(A.a*B%p,A.b*B%p);
}
}k[N][2];
struct edge {
int u,v,nx;
pt x,y;
edge(){}
edge(int u,int v,pt x,pt y,int nx):u(u),v(v),x(x),y(y),nx(nx){}
}e[N];
int fi[N],ec;
void ADD(int u,int v,pt x,pt y) {
e[++ec]=edge(u,v,x,y,fi[u]); fi[u]=ec;
}
LL g[N][2],bx[N][2],f[N][2],ans;
int pre(int x,int fa) {
vis[x]=1;
g[x][0]=g[x][1]=1;
int rs=0;
if(x==2874) {
int debug=1;
}
for(int i=fir[x];i;i=nxt[i]) if(!vis[to[i]]) {
int w=pre(to[i],x); if(!rs) rs=w;
if(!w) {
g[x][0]=g[x][0]*((g[to[i]][0]+g[to[i]][1])%p)%p;
g[x][1]=g[x][1]*g[to[i]][0]%p;
}
else if(is[x]) ADD(x,w,k[to[i]][0]+k[to[i]][1],k[to[i]][0]);
else k[x][0]=k[to[i]][0]+k[to[i]][1],k[x][1]=k[to[i]][0];
}
if(is[x]) k[x][0]=pt(1,0),k[x][1]=pt(0,1);
else k[x][0]=k[x][0]*g[x][0],k[x][1]=k[x][1]*g[x][1];
if(is[x]) rs=x;
return rs;
}
void dp(int x) {
f[x][0]=bx[x][1]?0:g[x][0];
f[x][1]=bx[x][0]?0:g[x][1];
for(int i=fi[x];i;i=e[i].nx) {
int v=e[i].v;
pt a=e[i].x,b=e[i].y;
dp(v);
f[x][0]=(f[x][0]*(a.a*f[v][0]%p+a.b*f[v][1]%p))%p;
f[x][1]=(f[x][1]*(b.a*f[v][0]%p+b.b*f[v][1]%p))%p;
}
}
//#define DEBUG
int main() {
#ifdef DEBUG
freopen("duliu.in","r",stdin);
freopen("duliu.out","w",stdout);
#endif
read(n); read(m);
For(i,1,m) {
int u,v;
read(u); read(v);
add(u,v);
}
dfs(1,0); is[1]=1;
memset(vis,0,sizeof(vis));
pre(1,0);
int nn=(1<<tot)-1;
For(i,0,nn) {
For(j,1,tot) {
if(i&(1<<j-1)) {
bx[eu[j]][1]=1; bx[ev[j]][0]=1;
}
else bx[eu[j]][0]=1;
}
dp(1);
ans=((ans+f[1][0])%p+f[1][1])%p;
For(j,1,tot) {
if(i&(1<<j-1)) {
bx[eu[j]][1]=0; bx[ev[j]][0]=0;
}
else bx[eu[j]][0]=0;
}
}
printf("%lld\n",ans);
return 0;
}
/*
12 17
12 3
12 5
11 12
7 5
5 10
1 5
5 8
8 7
11 9
3 6
10 1
1 9
1 7
12 4
2 9
11 2
7 3
*/
sxy的做法:
一个点能走到的是一段区间,记为l[x],r[x]
若一段路的钥匙在左端点及左边,那么只能从左走到右,从左到右连单向边.
若钥匙在右端点及右边,那么只能从右走到左,单向边
否则连双向边.预处理出这些边都能走的情况下左右最远走到哪里,记为ld,rd
然后从左到右计算l,r,
1.若我可以到左边,且钥匙在我这里或者不需要钥匙,那么l[x]=l[x-1],r[x]=r[x-1]
2.若我到不了左边,在x~rd[x]中二分一个位置pos,使这从x到pos这一段的每一个位置的钥匙都在从x到它之间,l[x]=x,r[x]=pos
3.我能到左边,拿不到钥匙.先和2一样二分一个pos,看在这一段中能不能拿到左边的钥匙,l[x]拓展到l[x-1],拿到这一段钥匙后继续往右边二分,然后再试图向左拓展
发现每次向左拓展的点一定是之前未拓展到的,那么每条向左的边最多会使我向左拓展一次,每次向右拓展复杂的是一个log,总复杂的nlogn
我的一个及其蠢的做法:
发现如果从x能走到y(假使x在y左边,右边同理),设x到y的路径上最靠左的钥匙位置在z,走过的路径即为z,x,y这一段
这一段路径要能走需要满足,x到y路径上任意一个点a,它的钥匙在的地方b,从x到b的每个点的钥匙必须在它右边且在a左边
那么对于每个点x,它的钥匙在它左边的y,可以二分一个最靠右的z使y到z的路径上所有点的钥匙都在自己的右边且在x的左边,记录这个最靠右的位置为rans[x]
那么x能走到y需要满足从x到y的rans都大于x
对于rans可以用线段树记录所有点钥匙所在的位置,在线段树上二分,求出rans后第二个问题可以用线段树解决.
同理考虑左边的lans
x在y右边的情况同理
时间复杂度nlogn,然而常数巨大..本机卡常卡过了,洛谷t了两个点
1 // luogu-judger-enable-o2
2 //Achen
3 #include<algorithm>
4 #include<iostream>
5 #include<cstring>
6 #include<cstdlib>
7 #include<vector>
8 #include<cstdio>
9 #include<queue>
10 #include<cmath>
11 #include<set>
12 #define For(i,a,b) for(register int i=(a);i<=(b);i++)
13 #define Rep(i,a,b) for(register int i=(a);i>=(b);i--)
14 const int N=1e6+7;
15 typedef long long LL;
16 typedef double db;
17 using namespace std;
18 int n,m,q,yl[N];
19
20 template<typename T> void read(T &x) {
21 char ch=getchar(); x=0; T f=1;
22 while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
23 if(ch=='-') f=-1,ch=getchar();
24 for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
25 }
26
27 #define lc x<<1
28 #define rc ((x<<1)|1)
29 #define mid ((l+r)>>1)
30 int mx[N<<2],mi[N<<2];
31 inline void build(int x,int l,int r) {
32 if(l==r) {
33 mx[x]=mi[x]=yl[l];
34 if(yl[l]==0) mx[x]=0,mi[x]=n+1;
35 return;
36 }
37 build(lc,l,mid); build(rc,mid+1,r);
38 mx[x]=max(mx[lc],mx[rc]);
39 mi[x]=min(mi[lc],mi[rc]);
40 }
41
42 inline int qryl(int x,int l,int r,int ql,int qr,int v) {
43 if(l>=ql&&r<=qr) {
44 if(mx[x]<=v) return r;
45 if(l==r) return 0;
46 if(mx[lc]<=v) {
47 int rs=qryl(rc,mid+1,r,ql,qr,v);
48 if(!rs) rs=mid;
49 return rs;
50 }
51 else return qryl(lc,l,mid,ql,qr,v);
52 }
53 int rs=0;
54 if(ql>mid) return qryl(rc,mid+1,r,ql,qr,v);
55 if(qr<=mid) return qryl(lc,l,mid,ql,qr,v);
56 else {
57 rs=qryl(lc,l,mid,ql,qr,v);
58 int rs2=0;
59 if(rs==mid) rs2=qryl(rc,mid+1,r,ql,qr,v);
60 if(rs2) rs=rs2;
61 return rs;
62 }
63 }
64
65 inline int qryr(int x,int l,int r,int ql,int qr,int v) {
66 if(l>=ql&&r<=qr) {
67 if(mi[x]>=v) return l;
68 if(l==r) return 0;
69 if(mi[rc]>=v) {
70 int rs=qryr(lc,l,mid,ql,qr,v);
71 if(!rs) rs=mid+1;
72 return rs;
73 }
74 else return qryr(rc,mid+1,r,ql,qr,v);
75 }
76 int rs=0;
77 if(ql>mid) return qryr(rc,mid+1,r,ql,qr,v);
78 if(qr<=mid) return qryr(lc,l,mid,ql,qr,v);
79 else {
80 rs=qryr(rc,mid+1,r,ql,qr,v);
81 int rs2=0;
82 if(rs==mid+1) rs2=qryr(lc,l,mid,ql,qr,v);
83 if(rs2) rs=rs2;
84 return rs;
85 }
86 }
87
88 int lans[N],rans[N];
89 int sgmi[N<<2],sgmx[N<<2];
90 inline void build2(int x,int l,int r) {
91 if(l==r) { sgmi[x]=rans[l]; sgmx[x]=lans[l]; return; }
92 build2(lc,l,mid); build2(rc,mid+1,r);
93 sgmi[x]=min(sgmi[lc],sgmi[rc]);
94 sgmx[x]=max(sgmx[lc],sgmx[rc]);
95 }
96
97 inline int qrymi(int x,int l,int r,int ql,int qr,int sg[]) {
98 if(l>=ql&&r<=qr) return sg[x];
99 if(qr<=mid) return qrymi(lc,l,mid,ql,qr,sg);
100 if(ql>mid) return qrymi(rc,mid+1,r,ql,qr,sg);
101 return min(qrymi(lc,l,mid,ql,qr,sg),qrymi(rc,mid+1,r,ql,qr,sg));
102 }
103
104 inline int qrymx(int x,int l,int r,int ql,int qr,int sg[]) {
105 if(l>=ql&&r<=qr) return sg[x];
106 if(qr<=mid) return qrymx(lc,l,mid,ql,qr,sg);
107 if(ql>mid) return qrymx(rc,mid+1,r,ql,qr,sg);
108 return max(qrymx(lc,l,mid,ql,qr,sg),qrymx(rc,mid+1,r,ql,qr,sg));
109 }
110
111 //#define DEBUG
112 int main() {
113 #ifdef DEBUG
114 freopen("game.in","r",stdin);
115 freopen("game.out","w",stdout);
116 #endif
117 read(n); read(m); read(q);
118 For(i,1,m) {
119 int x,y;
120 read(x); read(y);
121 yl[x]=y;
122 }
123 build(1,1,n-1);
124 For(i,1,n) {
125 if(yl[i]==0) {
126 lans[i]=0;
127 rans[i]=n+1;
128 }
129 else {
130 if(yl[i]>i)
131 lans[i]=qryr(1,1,n-1,1,yl[i]-1,i+1);
132 else lans[i]=n+1;
133 if(yl[i]<=i)
134 rans[i]=qryl(1,1,n-1,yl[i],n,i);
135 else rans[i]=0;
136 }
137 }
138 build2(1,1,n-1);
139 For(i,1,q) {
140 int x,y;
141 read(x); read(y);
142 if(x==y) puts("YES");
143 else if(x<=y) {
144 int z=qrymi(1,1,n-1,x,y-1,mi);
145 int tp1=qrymi(1,1,n-1,x,y-1,sgmi);
146 int tp2=z>x-1?x:qrymx(1,1,n-1,z,x-1,sgmx);
147 if(tp1>=max(1,x-1)&&tp2<=min(n,x+1)) puts("YES");
148 else puts("NO");
149 }
150 else {
151 int z=qrymx(1,1,n-1,y,x-1,mx);
152 int tp2=qrymx(1,1,n-1,y,x-1,sgmx);
153 int tp1=z-1<x?x:qrymi(1,1,n-1,x,z-1,sgmi);
154 if(tp1>=max(1,x-1)&&tp2<=min(n,x+1)) puts("YES");
155 else puts("NO");
156 }
157 }
158 return 0;
159 }
正解:按sxy的做法把图建出来后,按拓扑序拓展,似乎就可以直接做到O(n)了
读题读好久..转换过来就是,给一棵树,父亲必须比儿子先选,选出来的序列记为p,答案为i*p[i]的和
贪心
考虑合并两段区间a,b,区间内已经合并好了,若a放在b前面更优,则说明len[a]*val[b]>len[b]*val[a]
即len[a]/val[a]>len[b]/val[b];
那么优先队列存每段区间的len和val,每次取出队首和父亲合并即可.
1 //Achen
2 #include<algorithm>
3 #include<iostream>
4 #include<cstring>
5 #include<cstdlib>
6 #include<vector>
7 #include<cstdio>
8 #include<queue>
9 #include<cmath>
10 #include<set>
11 #define For(i,a,b) for(int i=(a);i<=(b);i++)
12 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
13 const int N=1000007;
14 typedef long long LL;
15 typedef double db;
16 using namespace std;
17 int n,a[N],w[N],fa[N],len[N];
18 LL sum[N];
19
20 template<typename T> void read(T &x) {
21 char ch=getchar(); x=0; T f=1;
22 while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
23 if(ch=='-') f=-1,ch=getchar();
24 for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
25 }
26
27 struct node {
28 int x,sz; LL sum;
29 node(int x,int sz,LL sum):x(x),sz(sz),sum(sum){}
30 friend bool operator <(const node&A,const node&B) {
31 return A.sz*B.sum<B.sz*A.sum;
32 }
33 };
34
35 int ecnt,fir[N],nxt[N],to[N],in[N];
36 void add(int u,int v) {
37 nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; in[v]++;
38 }
39
40 queue<int>q;
41 LL tpsort() {
42 int tot=0;
43 For(i,1,n) if(!in[i]) q.push(i);
44 while(!q.empty()) {
45 int x=q.front();
46 q.pop();
47 tot++;
48 for(int i=fir[x];i;i=nxt[i]) {
49 in[to[i]]--;
50 if(!in[to[i]]) q.push(to[i]);
51 }
52 }
53 if(tot!=n) return -1;
54 return 1;
55 }
56
57 int f[N];
58 int find(int x) { return x==f[x]?f[x]:f[x]=find(f[x]); }
59
60 priority_queue<node>que;
61 LL solve() {
62 LL rs=0;
63 For(i,1,n) rs+=w[i],f[i]=i;
64 while(!que.empty()) {
65 node tp=que.top();
66 que.pop();
67 if(tp.sz!=len[tp.x]) continue;
68 int F=find(fa[tp.x]);
69 rs+=sum[tp.x]*len[F];
70 sum[F]+=sum[tp.x];
71 len[F]+=len[tp.x];
72 f[tp.x]=F;
73 if(F) que.push(node(F,len[F],sum[F]));
74 }
75 return rs;
76 }
77
78 //#define DEBUG
79 int main() {
80 #ifdef DEBUG
81 freopen("perm.in","r",stdin);
82 freopen("perm.out","w",stdout);
83 #endif
84 read(n);
85 For(i,1,n) {
86 read(a[i]);
87 if(a[i]) { fa[i]=a[i]; add(a[i],i); }
88 }
89 For(i,1,n) read(w[i]);
90 if(tpsort()==-1) puts("-1");
91 else {
92 For(i,1,n) {
93 sum[i]=w[i]; len[i]=1;
94 que.push(node(i,1,sum[i]));
95 }
96 printf("%lld\n",solve());
97 }
98 return 0;
99 }
一道水题,直接树形dp,记录上面的公路和铁路的条数.
一开始不知道为什么以为是满二叉树,空间炸了.
深度有40,不炸空间要开map存.
1 //Achen
2 #include<algorithm>
3 #include<iostream>
4 #include<cstring>
5 #include<cstdlib>
6 #include<vector>
7 #include<cstdio>
8 #include<queue>
9 #include<cmath>
10 #include<set>
11 #include<map>
12 #define For(i,a,b) for(int i=(a);i<=(b);i++)
13 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
14 const int N=40007;
15 typedef long long LL;
16 typedef double db;
17 using namespace std;
18 LL n,a[N],b[N],c[N],gls[N],tls[N];
19 map<int,LL>dp[N];
20
21 template<typename T> void read(T &x) {
22 char ch=getchar(); x=0; T f=1;
23 while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
24 if(ch=='-') f=-1,ch=getchar();
25 for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
26 }
27
28 void add(int u,int v,int o) {
29 if(!o) gls[u]=v;
30 else tls[u]=v;
31 }
32
33 void dfs(int x,int l1,int l2) { //i gonglu
34 if(!gls[x]) {
35 For(i,0,l1) For(j,0,l2) dp[x][i*100+j]=c[x]*(a[x]+i)*(b[x]+j);
36 return ;
37 }
38 int gl=gls[x],tl=tls[x];
39 dfs(gl,l1+1,l2); dfs(tl,l1,l2+1);
40 For(i,0,l1) For(j,0,l2)
41 dp[x][i*100+j]=min(dp[gl][i*100+j]+dp[tl][i*100+j+1],dp[gl][(i+1)*100+j]+dp[tl][i*100+j]);
42 }
43
44 //#define DEBUG
45 int main() {
46 #ifdef DEBUG
47 freopen("road.in","r",stdin);
48 freopen("road.out","w",stdout);
49 #endif
50 read(n);
51 For(i,1,n-1) {
52 int si,ti;
53 read(si); read(ti);
54 if(si>0) add(i+n,n+si,0);
55 else add(i+n,-si,0);
56 if(ti>0) add(i+n,n+ti,1);
57 else add(i+n,-ti,1);
58 }
59 For(i,1,n) { read(a[i]); read(b[i]); read(c[i]); }
60 dfs(1+n,0,0);
61 printf("%lld\n",dp[n+1][0]);
62 return 0;
63 }
64 /*
65 6
66 2 3
67 4 5
68 -1 -2
69 -3 -4
70 -5 -6
71 1 2 3
72 1 3 2
73 2 1 3
74 2 3 1
75 3 1 2
76 3 2 1
77
78 9
79 2 -2
80 3 -3
81 4 -4
82 5 -5
83 6 -6
84 7 -7
85 8 -8
86 -1 -9
87 1 60 1
88 1 60 1
89 1 60 1
90 1 60 1
91 1 60 1
92 1 60 1
93 1 60 1
94 1 60 1
95 1 60 1
96
97 12
98 2 4
99 5 3
100 -7 10
101 11 9
102 -1 6
103 8 7
104 -6 -10
105 -9 -4
106 -12 -5
107 -2 -3
108 -8 -11
109 53 26 491
110 24 58 190
111 17 37 356
112 15 51 997
113 30 19 398
114 3 45 27
115 52 55 838
116 16 18 931
117 58 24 212
118 43 25 198
119 54 15 172
120 34 5 524
121 */
Orz cai大佬day2AK进队
Orz sxyday2AK吊打我这种辣鸡滚粗选手
你们都太强啦%%%