20180225小测
今天早晨一来就是一场猝不及防的考试(说好的下午考呢?)
于是没有打卡(其实打了也是模拟赛爆零)
T1:
这题一看不可做啊......
数据范围显然是nlogn,大概复杂度全都在排序。
好,我们开始推结论什么的,一个人能感染到的人一定是前面比他快或者后面比他慢。
然后呢?感染有时间顺序,所以我们能按照时间建图,存在点数O(n^3)的大力DP。
but,多个人同时相遇怎么办?不会DP了,弃坑弃坑。
写了10分爆搜然后写挂爆零了。
关于这题正解?
如果我们让时间趋于正无穷,那么这些人的顺序一定是按照速度排序的。
考虑一个人能感染那些人?起始点在他前且速度比他快的,起始点在他后且比他慢的。
那么,每个人在这个序列上能感染的区间就是[起始点比在他后的人且比他慢的中最慢的,起始点在他前的人且比他快的中最快的]。
为什么?显然这个区间外的人都不能被感染(那些人一定是在前且慢或者在后且块)。
对于区间内速度大于当前点的点,如果速度比右端点慢且在初始位置当前点右,那么他会被已经被感染的右端点感染。
如果速度比左端点慢且初始位置在左,那么因为他比当前点快,一定会被当前点直接感染。
左边情况同理。
考场爆零代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 const int maxn=17; 6 const double eps=1e-8; 7 8 struct Node { 9 double t; 10 int u,v; 11 Node() {} 12 Node(double tt,int uu,int vv) { t = tt , u = uu , v = vv ; } 13 friend bool operator < (const Node &a,const Node &b) { 14 return a.t < b.t; 15 } 16 }ns[maxn*maxn]; 17 18 int x[maxn],v[maxn],used[maxn],vis[maxn],n,cnt,ans; 19 20 inline void gnode(int a,int b) { 21 if( x[a] < x[b] && v[a] < v[b] ) return; 22 ns[++cnt] = Node((double)(x[b]-x[a])/(v[a]-v[b]),a,b); 23 } 24 inline void check() { 25 memcpy(vis+1,used+1,sizeof(int)*n); 26 for(int i=1,j,tp;i<=cnt;i=j+1) { 27 j = i , tp = 0; 28 while( j < cnt && std::fabs(ns[j+1].t-ns[i].t) <= eps ) ++j; 29 for(int k=i;k<=j;k++) tp |= ( vis[ns[k].u] | vis[ns[k].v] ); 30 for(int k=i;k<=j;k++) vis[ns[k].u] |= tp , vis[ns[k].v] |= tp; 31 } 32 for(int i=1;i<=n;i++) if( !vis[i] ) return; 33 ++ans; 34 } 35 inline void dfs(int pos) { 36 if( pos > n ) return check(); 37 used[pos] = 0 , dfs( pos + 1 ) , 38 used[pos] = 1 , dfs( pos + 1 ) ; 39 } 40 41 int main() { 42 scanf("%d",&n); 43 for(int i=1;i<=n;i++) scanf("%d%d",x+i,v+i); 44 for(int i=1;i<=n;i++) 45 for(int j=i+1;j<=n;j++) 46 gnode(i,j); 47 std::sort(ns+1,ns+1+cnt); 48 dfs(1); 49 printf("%d\n",ans); 50 return 0; 51 }
考后AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define lli long long int 6 #define debug cout 7 using namespace std; 8 const int maxn=2e5+1e2; 9 const int mod = 1e9 + 7; 10 11 struct Person { 12 int s,v,id; 13 friend bool operator < (const Person &a,const Person &b) { 14 return a.v < b.v; 15 } 16 }ns[maxn]; 17 18 19 struct Segment { 20 int l,r; 21 friend bool operator < (const Segment &a,const Segment &b) { 22 return a.r != b.r ? a.r < b.r : a.l < b.l; 23 } 24 }ss[maxn]; 25 26 int mov[maxn]; 27 lli ff[maxn],sum[maxn],pows[maxn],*f; 28 29 inline bool cmp(const Person &a,const Person &b) { 30 return a.s < b.s; 31 } 32 33 int main() { 34 static int n; 35 scanf("%d",&n); 36 for(int i=1;i<=n;i++) scanf("%d%d",&ns[i].s,&ns[i].v); 37 sort(ns+1,ns+1+n,cmp); 38 for(int i=1;i<=n;i++) ns[i].id = i; 39 sort(ns+1,ns+1+n); 40 for(int i=1;i<=n;i++) mov[ns[i].id] = i; 41 sort(ns+1,ns+1+n,cmp); 42 int m = 0; 43 for(int i=1;i<=n;i++) { 44 m = max( m , mov[i] ); 45 ss[i].r = m; 46 } 47 m = n+1; 48 for(int i=n;i;i--) { 49 m = min( m , mov[i] ); 50 ss[i].l = m; 51 } 52 sort(ss+1,ss+1+n); 53 *pows = 1 , f = ff + 1; 54 for(int i=1;i<=n;i++) pows[i] = pows[i-1] * 2 % mod; 55 *f = *sum = 1; 56 for(int i=1,l=1,r=1;i<=n;i++) { 57 if( ss[l].r == i ) { 58 r = l; 59 while( r < n && ss[r+1].r == i ) ++r; 60 for(int k=l;k<=r;k++) f[i] = ( f[i] + ( sum[i-1] - sum[ss[k].l-2] ) * pows[r-k] % mod ) % mod; 61 l = r + 1; 62 } 63 sum[i] = ( sum[i-1] + f[i] ) % mod; 64 } 65 printf("%lld\n",(f[n]+mod)%mod); 66 return 0; 67 }
T2:
有一个奇怪的条件:存在药和药材的完美匹配。
这是什么意思呢?就是当我们选择某种药的时候,必须选择其药材匹配的所有药。
为什么呢?考虑当前你选出的集合,每个都匹配一种药材。如果你没有选择某个没有被匹配的药材对应的药,则这些药包含的药材多一种,不符合条件。
所以一个二分图+一个最大权闭合子图就完了。
考场AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 using namespace std; 7 const int maxn=3e2+1e1,maxe=maxn*maxn; 8 const int inf = 0x3f3f3f3f; 9 10 int val[maxn],n,st,ed,ans; 11 12 namespace Bingraph { 13 int in[maxn][maxn],fa[maxn]; 14 bool vis[maxn]; 15 16 inline bool dfs(int pos) { // pos is left point which has value . 17 for(int i=1;i<=n;i++) if( in[pos][i] && !vis[i] ) { 18 vis[i] = 1; 19 if( !fa[i] || dfs(fa[i]) ) { 20 fa[i] = pos; 21 return 1; 22 } 23 } 24 return 0; 25 } 26 inline void pir() { 27 for(int i=1;i<=n;i++) { 28 memset(vis,0,sizeof(vis)); 29 dfs(i); 30 } 31 } 32 } 33 34 namespace Flow { 35 int s[maxn],t[maxe<<2],nxt[maxe<<2],f[maxe<<2],dep[maxn]; 36 37 inline void coredge(int from,int to,int flow) { 38 static int cnt = 1; 39 t[++cnt] = to , f[cnt] = flow , 40 nxt[cnt] = s[from] , s[from] = cnt; 41 } 42 inline void singledge(int from,int to,int flow) { 43 coredge(from,to,flow) , coredge(to,from,0); 44 } 45 inline bool bfs() { 46 memset(dep,-1,sizeof(dep)) , dep[st] = 0; 47 queue<int> q; q.push(st); 48 while( q.size() ) { 49 const int pos = q.front(); q.pop(); 50 for(int at=s[pos];at;at=nxt[at]) 51 if( f[at] && !~dep[t[at]] ) 52 dep[t[at]] = dep[pos] + 1 , q.push(t[at]); 53 } 54 return ~dep[ed]; 55 } 56 inline int dfs(int pos,int flow) { 57 if( pos == ed ) return flow; 58 int ret = 0 , now = 0; 59 for(int at=s[pos];at;at=nxt[at]) 60 if( f[at] && dep[t[at]] > dep[pos] ) { 61 now = dfs(t[at],min(flow,f[at])); 62 ret += now , flow -= now , 63 f[at] -= now , f[at^1] += now; 64 if( !flow ) return ret; 65 } 66 if( !ret ) dep[pos] = -1; 67 return ret; 68 } 69 inline int dinic() { 70 int ret = 0 , now = 0; 71 while( bfs() ) { 72 while( now = dfs(st,inf) ) ret += now; 73 } 74 return ret; 75 } 76 } 77 78 inline void build() { 79 using namespace Flow; 80 using namespace Bingraph; 81 st = n + 1 , ed = st + 1; 82 for(int i=1;i<=n;i++) val[i] = -val[i]; 83 for(int i=1;i<=n;i++) { 84 if( val[i] > 0 ) { 85 ans += val[i]; 86 singledge(st,i,val[i]); 87 } else singledge(i,ed,-val[i]); 88 } 89 for(int i=1;i<=n;i++) 90 for(int j=1;j<=n;j++) 91 if( in[i][j] ) { 92 singledge(i,fa[j],inf); 93 } 94 } 95 96 int main() { 97 scanf("%d",&n); 98 for(int i=1,t,x;i<=n;i++) { 99 scanf("%d",&t); 100 while( t-- ) { 101 scanf("%d",&x); 102 Bingraph::in[i][x] = 1; 103 } 104 } 105 for(int i=1;i<=n;i++) scanf("%d",val+i); 106 Bingraph::pir(); 107 build(); 108 ans -= Flow::dinic(); 109 printf("%d\n",-ans); 110 return 0; 111 }
T3:
看到n=1k换模数,直接就想NTT卷积了,然后发现不可做。
输出样例和随机数,立下flag说如果能多过一个点就女装。
然而一个点也没有多过。看来是天不让我女装(我也没办法是吧)。(不)
我们枚举层数为奇数的点,考虑层数为奇数的点和层数为偶数的点构成一个二分图,然后这个二分图间可以随意连边,求这个完全二分图的生成树个数。
这不是"BZOJ4766: 文艺计算姬"吗?直接n^(m-1)*m^(n-1)就好。
证明可以用prufer序列证明,n左边点出现m-1次,m个右边点出现n-1次,乘法原理即可。
别忘了因为我们钦定1号点为根,所以组合数是C(n-1,k-1)。
考场5分代码:
1 #include<cstdio> 2 #include<cstdlib> 3 4 int main() { 5 int n,k; 6 scanf("%d%d",&n,&k); 7 if( n == 4 && k == 2 ) puts("12"); 8 else { 9 srand((unsigned long long)new char); 10 printf("%d\n",rand()%(n*n)+1); 11 } 12 return 0; 13 }
考后AC代码:
1 #include<cstdio> 2 #define lli long long int 3 4 lli n,k,mod,ans; 5 6 inline lli fastpow(lli base,lli tim,lli mod) { 7 lli now = base % mod , ret = 1; 8 while( tim ) { 9 if ( tim & 1 ) ret = ret * now % mod; 10 if( tim >>= 1 ) now = now * now % mod; 11 } 12 return ret % mod; 13 } 14 inline lli c(lli n,lli m) { 15 lli inv = 1 , ret = 1; 16 for(lli i=n;i>n-m;i--) ret = ret * i % mod; 17 for(lli i=1;i<=m;i++) inv = inv * i % mod; 18 return ret * fastpow(inv,mod-2,mod) % mod; 19 } 20 21 int main() { 22 scanf("%lld%lld%lld",&n,&k,&mod); 23 ans = c(n-1,k-1) * fastpow(n-k,k-1,mod) % mod * fastpow(k,n-k-1,mod) % mod; 24 printf("%lld\n",ans); 25 return 0; 26 }
这次考试两个智商题一个不会,外加暴力写挂简直,果然还是我太弱了啊。
真·爆零蒟蒻XZY