10.21 模拟赛
第一次140,好好的200
mmp 因为T1少写一个取MOD
T1:
一棵黑白树,已知原先树上共n 个点,每个点都是黑点或者白点,切去若干条边后,分成的若干个连通子树中每块恰有一个黑点,请问有多少种切树的方案满足这个条件
两种方案不同当且仅当存在一条边在其中一个方案中被切而在另一个方案中没被切
思路:
树形dp
设dp i 0/1 表示第i个节点,它向上贡献0/1个黑点的方案数
对于每个节点
若它为白色
则dp i 0 可以转移为它的子节点的dp 0值之积
dp i 1 可以转移为它的dp 0 值除以每个子节点的白色再乘以该节点的黑色的和 因为你要从子节点里找一个黑的拉出来
若它为黑色
则dp i 1 可以转移为它的子节点的dp 0值之积
dp i 0 等于它的dp i 1 因为这个点贡献只能贡献它自己
1 #include<iostream>
2 #include<cstdio>
3 #include<cmath>
4 #include<cstring>
5 #include<algorithm>
6 #include<cstdlib>
7 #include<vector>
8 #include<queue>
9 #define ll long long
10 #define inf 2147483611
11 #define MAXN 101010
12 #define MOD 1000000007
13 using namespace std;
14 inline int read()
15 {
16 int x=0,f=1;char ch=getchar();
17 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
18 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
19 return x*f;
20 }
21 int n;
22 int first[MAXN],next[2*MAXN],to[2*MAXN],cnt,f[MAXN][2];
23 bool k[MAXN];
24 void add(int u,int v) {next[++cnt]=first[u],first[u]=cnt,to[cnt]=v;}
25 void exgcd(ll a,ll b,ll& d,ll& x,ll& y)
26 {
27 if(!b) {d=a,x=1,y=0;}
28 else {exgcd(b,a%b,d,y,x);y-=x*(a/b);}
29 }
30 ll inv(ll a, ll p)
31 {
32 ll d,x,y;
33 exgcd(a,p,d,x,y);
34 return d==1?(x+p)%p:-1;
35 }
36 void dp(int x,int fa)
37 {
38 if(k[x])
39 {
40 f[x][1]=f[x][0]=1;
41 for(int i=first[x];i;i=next[i])
42 {
43 if(to[i]==fa) continue;
44 dp(to[i],x);
45 if(f[to[i]][0]) (f[x][1]*=f[to[i]][0])%=MOD;
46 }
47 f[x][0]=f[x][1];
48 }
49 else
50 {
51 f[x][1]=0,f[x][0]=1;
52 for(int i=first[x];i;i=next[i])
53 {
54 if(to[i]==fa) continue;
55 dp(to[i],x);
56 if(f[to[i]][0]) (f[x][0]*=f[to[i]][0])%=MOD;
57 }
58 for(int i=first[x];i;i=next[i])
59 {
60 if(to[i]==fa) continue;
61 if(f[to[i]][1]) (f[x][1]+=((f[x][0]*inv(f[to[i]][0],MOD))%MOD*f[to[i]][1])%MOD)%=MOD;
62 }
63 (f[x][0]+=f[x][1])%=MOD;
64 }
65 }
66 int main()
67 {
68 freopen("cut.in","r",stdin);
69 freopen("cut.out","w",stdout);
70 n=read();
71 int a,b;
72 for(int i=1;i<=n;i++) {a=read(),k[i]=a;}
73 for(int i=1;i<n;i++) {a=read(),b=read();add(a,b);add(b,a);}
74 dp(1,0);
75 printf("%d",f[1][1]);
76 }
orz 因为有取模和除法,所以搞了一个逆元
T2:
一张连通无向图,起点1号点,终点n 号点,每条道路有一个困难度vi,定义一条路径的疲劳度为他路上经过的所有道路的困难度的最大值
一开始有k 点体力,在通过一条道路时,他可以选择消耗若干点体力值,每消耗一点,道路的困难度也会降低1
但一条道路的困难度不能低于0 求这次从1到n的最小疲劳度
思路:
二分答案
对于每个二分出来的疲劳值,我们把所有边的困难度都减去这个值,然后跑一下最短路
看看最后dis n是否<=k
若<=k则疲劳度还可以更小
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #include<cstdlib> 7 #include<vector> 8 #include<queue> 9 #define ll long long 10 #define inf 2147483611 11 #define MAXN 50101 12 using namespace std; 13 inline int read() 14 { 15 int x=0,f=1;char ch=getchar(); 16 while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} 17 while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();} 18 return x*f; 19 } 20 int n,m,k; 21 int first[MAXN],next[2*MAXN],to[2*MAXN],val[2*MAXN],aval[2*MAXN],cnt,dis[MAXN]; 22 bool vis[MAXN]; 23 void add(int u,int v,int d) {next[++cnt]=first[u],first[u]=cnt,to[cnt]=v,val[cnt]=d;} 24 void spfa() 25 { 26 queue<int> q; 27 memset(dis,127,sizeof(dis)); 28 memset(vis,0,sizeof(vis)); 29 q.push(1);vis[1]=1;dis[1]=0; 30 while(!q.empty()) 31 { 32 int tmp=q.front(); 33 q.pop(); 34 vis[tmp]=0; 35 for(int i=first[tmp];i;i=next[i]) 36 if(dis[to[i]]>dis[tmp]+aval[i]) 37 { 38 dis[to[i]]=dis[tmp]+aval[i]; 39 if(!vis[to[i]]) {vis[to[i]]=1;q.push(to[i]);} 40 } 41 } 42 } 43 int main() 44 { 45 freopen("bicycling.in","r",stdin); 46 freopen("bicycling.out","w",stdout); 47 n=read(),m=read(),k=read(); 48 int a,b,c; 49 int l=0,r=0,mid; 50 for(int i=1;i<=m;i++) {a=read(),b=read(),c=read();add(a,b,c);add(b,a,c);r=max(r,c);} 51 while(l<r-1) 52 { 53 mid=(l+r)>>1; 54 for(int i=1;i<=cnt;i++) aval[i]=max(val[i]-mid,0); 55 spfa(); 56 if(dis[n]<=k) r=mid; 57 else l=mid; 58 } 59 printf("%d",r); 60 }
T3:
结论题
防ak好题