北京集训:20180321

动态题目问题,我们需要自动AC机......

T1:


看到划掉的那部分了吗,没错,考试中途改题面了......
然而我只会写15分爆搜......
计算几何+构造,不改了,爆搜滚粗了......
以下为官方题解:


15分爆搜代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<cstdlib>
 7 #define debug cout
 8 typedef long long int lli;
 9 using namespace std;
10 const int maxn=1e4+1e1;
11 const double pi = acos(-1.0);
12 
13 struct Point {
14     lli x,y;
15     inline friend Point operator - (const Point &a,const Point &b) {
16         return (Point){a.x-b.x,a.y-b.y};
17     }
18     inline friend lli operator * (const Point &a,const Point &b) {
19         return a.x * b.y - a.y * b.x;
20     }
21     inline double dis() const {
22         return sqrt(x*x+y*y);
23     }
24     friend bool operator < (const Point &a,const Point &b) {
25         return a.x < b.x;
26     }
27 }ps[maxn];
28 int a[maxn],b[maxn],cnt;
29 bool used[maxn];
30 long double sum;
31 int n;
32 
33 inline bool iscorss(const Point &pa,const Point &pb,const Point &ta,const Point &tb) {
34     if( min(ta.x,tb.x) > max(pa.x,pb.x)
35      || min(pa.x,pb.x) > max(ta.x,tb.x)
36      || min(ta.y,tb.y) > max(pa.y,pb.y)
37      || min(pa.y,pb.y) > max(ta.y,tb.y) )
38         return 0;
39     return ( (__int128)((tb-pa)*(pb-pa)) * ((pb-pa)*(ta-pa)) > 0 && (__int128)((tb-pb)*(pa-pb)) * ((pa-pb)*(ta-pb)) > 0 );
40 }
41 inline bool judge(const int &nx,const int &ny) {
42     for(int i=1;i<=cnt;i++) {
43         if( iscorss(ps[a[i]],ps[b[i]],ps[nx],ps[ny]) ) return 0;
44     }
45     return 1;
46 }
47 inline bool checkans() {
48     double ret = 0;
49     for(int i=1;i<=cnt;i++) ret += ( ps[a[i]] - ps[b[i]] ).dis();
50     return ret * pi >= sum * 2.0;
51 }
52 inline void finish() {
53     if( !checkans() ) return;
54     for(int i=1;i<=cnt;i++) printf("%d %d\n",a[i],b[i]);
55     exit(0);
56 }
57 inline void dfs(int pos) {
58     if( pos > n << 1 ) return finish();
59     if( used[pos] ) return dfs(pos+1);
60     used[pos] = 1;
61     for(int i=pos+1;i<=n<<1;i++) if( !used[i] && judge(pos,i) ) {
62         used[i] = 1 , a[++cnt] = pos , b[cnt] = i;
63         dfs(pos+1);
64         used[i] = 0 , --cnt;
65     }
66     used[pos] = 0;
67 }
68 
69 int main() {
70     scanf("%d",&n);
71     if( n > 15 ) return 0;
72     for(int i=1;i<=n<<1;i++) scanf("%lld%lld",&ps[i].x,&ps[i].y);
73     for(int i=1,a,b;i<=n;i++) {
74         scanf("%d%d",&a,&b) ,
75         sum += ( ps[a] - ps[b] ).dis();
76     }
77     dfs(1);
78     return 0;
79 }
View Code

标程代码:

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3  
  4 #define db int
  5 #define eps 0
  6 struct point{db x,y;};
  7 point operator +(point a,point b){return (point){a.x+b.x,a.y+b.y};}
  8 point operator -(point a,point b){return (point){a.x-b.x,a.y-b.y};}
  9 bool operator < (const point &a,const point &b){return a.x==b.x? a.y<b.y:a.x<b.x;}
 10 long long operator *(point a,point b){return a.x*1LL*b.y-b.x*1LL*a.y;}
 11 db sg(long long x){return (x>-eps) - (x<eps);}
 12 db sideofb(point a,point b){return sg(a*b);}//-1 left
 13 bool a_onleft(point a,point b){return a*b<-eps;}
 14 long double dis(point a,point b){
 15     return sqrt((a.x-b.x)*1LL*(a.x-b.x)+(a.y-b.y)*1LL*(a.y-b.y));
 16 }
 17 #undef db
 18 #undef eps
 19 #define eps 1e-10
 20 #define N 10005
 21 point p[N];
 22 int n,mat[N],col[N];
 23 double theta[N],cval[N],sval[N];
 24 double d[N];
 25 const double pi=acos(-1);
 26 double reg(double ang){
 27     while(ang<0) ang+=pi;
 28     while(ang>pi) ang-=pi;
 29     return ang;
 30 }
 31 double tot=0,limit=2/pi;
 32 double getans(double ang){
 33     long double a0=0,a1=0;//a0cosx - a1cosx
 34     for(int i=1;i<=n;i++){
 35         if(i>mat[i]) continue;
 36         if(cos(theta[i]-ang)<0) a0-=cval[i],a1-=sval[i];
 37         else a0+=cval[i],a1+=sval[i];
 38     }
 39     long double ret=atan(a1/a0),mL=0;
 40     if(a0<eps&&a0>-eps) ret=pi/2;
 41     mL=a0*cos(ret)+a1*sin(ret);
 42     if(mL<0)
 43         mL=-mL,ret=ret+pi;
 44     if(mL>=limit*tot) return reg(ret);
 45     else return -1;
 46 }
 47 double findBestAngle(){
 48     vector<double> div;tot=0;
 49     for(int i=1;i<=n;i++)
 50         if(mat[i]>i){
 51             point t=p[mat[i]]-p[i];
 52             theta[i]=atan2(t.y,t.x);
 53             div.push_back(reg(theta[i]-pi/2));
 54             tot+= (d[i]=dis(p[mat[i]],p[i]));
 55             cval[i]=cos(theta[i])*d[i];
 56             sval[i]=sin(theta[i])*d[i];
 57         }
 58     div.push_back(0);div.push_back(pi);
 59     sort(div.begin(),div.end());
 60      
 61     double ret=-1;
 62     for(int i=1;i<div.size();i++){
 63         if(div[i]-div[i-1]>1e-8){
 64             ret=getans((div[i]+div[i-1])/2);
 65             if(ret>=0) return ret;
 66         }
 67     }
 68     assert(false);
 69 }
 70  
 71 int arr[N],base;
 72 bool cmp(int a,int b){
 73     return sideofb(p[a]-p[base],p[b]-p[base])<0;
 74 }
 75 void solve(int l,int r){
 76     int mn=l;
 77     for(int i=l+1;i<=r;i++)
 78         if(p[arr[i]]<p[arr[mn]]) mn=i;
 79     swap(arr[mn],arr[l]);base=arr[l];
 80     sort(arr+l+1,arr+r+1,cmp);
 81     int best=l-1;
 82     for(int i=l+1,pre=col[arr[l]];i<=r;i++){
 83         pre+=col[arr[i]];
 84         if(pre==0&&col[arr[i]]+col[arr[l]]==0)
 85             if(min(r-i,i-l)>min(r-best,best-l)) best=i;
 86     }
 87     mat[arr[l]]=arr[best],mat[arr[best]]=arr[l];
 88     if(best!=l+1) solve(l+1,best-1);
 89     if(best!=r) solve(best+1,r);
 90     return;
 91 }
 92  
 93 void readin(){
 94     scanf("%d",&n);n*=2;
 95     for(int i=1;i<=n;i++)
 96         scanf("%d%d",&p[i].x,&p[i].y);
 97     for(int i=1,f,t;i<=n/2;i++)
 98         scanf("%d%d",&f,&t),mat[f]=t,mat[t]=f;
 99 }
100  
101 void solve() {
102     double ang=findBestAngle(),px=cos(ang),py=sin(ang);
103     vector<pair<double,int> > fl; 
104     for(int i=1;i<=n;i++)
105         fl.push_back(make_pair(px*p[i].x+py*p[i].y,i));
106     sort(fl.begin(),fl.end() );
107     for(int i=0;i<n;i++)
108         col[fl[i].second]=(i<n/2? 1:-1);
109      
110     for(int i=1;i<=n;i++) arr[i]=i;
111     solve(1,n);
112      
113     for(int i=1;i<=n;i++)
114         if(i<mat[i])
115             printf("%d %d\n",i,mat[i]);
116 }
117  
118 int main(){
119     readin();
120     solve();
121     return 0;
122 }
View Code


T2:


5分边权求和乘2,10分代价减去直径......
好了,反正是欢乐赛,可以滚粗了。
考虑55分怎么写,我们只需要一个O(nk)的算法
像堆贪心一样每次把选择的路径的价值取反,然后重新找最长链并选择,中间重合的部分选择了两次相当于没选。
我们这样做最多k次就能得出方案。
最长链必须用树状DP求,两遍dfs的方法依赖了距离的单调性,很容易构造反例。
同时不能让second默认-1,因为可能有强行更新的情况。(其实我们用max和每个子树去更新就好了)
然后,这样想就想不出正解了......
考虑如果我们知道一个坐车的价值,如何计算出遍历整棵树最小代价和需要的坐车次数?
树状DP,f[i][0/1]表示遍历i的子树,最终回到i的时候是否坐车,需要的最小价值和坐车次数(记录二元组)。
枚举四种遍历方式进行转移。
如果计算出的坐车次数比k大怎么办?
我们提高坐车代价,显然次数是单调的,然后我们找到坐车次数恰好小于等于k的那个点。
这个点所代表的值,就是每次坐车能替换的最大代价。
然后我们用dp出的代价减去k*差值即可(我的代码里仅计算单程代价,因为每条边必须走路经过一遍,可以提前求和)。
明明前几天刚做过思路相同的题目的,为什么不会?
http://www.lydsy.com/JudgeOnline/problem.php?id=2654
考场10分代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 
 7 int n,k,c;
 8 
 9 namespace Case1 {
10     inline void work() {
11         int sum = 0;
12         for(int i=1,w;i<n;i++) scanf("%*d%*d%d",&w) , sum += w;
13         printf("%d\n",sum<<1);
14     }
15 }
16 namespace Case2 {
17     const int maxn=2e2+1e1;
18     int s[maxn],t[maxn<<1],nxt[maxn<<1],l[maxn<<1],dep[maxn],cnt,mxp;
19     inline void addedge(int from,int to,int len) {
20         t[++cnt] = to , l[cnt] = len ,
21         nxt[cnt] = s[from] , s[from] = cnt;
22     }
23     inline void dfs(int pos,int fa) {
24         if( dep[pos] > dep[mxp] ) mxp = pos;
25         for(int at=s[pos];at;at=nxt[at]) if( t[at] != fa ) {
26             dep[t[at]] = dep[pos] + l[at] , dfs(t[at],pos);
27         }
28     }
29     inline void work() {
30         int sum = 0;
31         memset(s,0,sizeof(s)) , cnt = 0;
32         for(int i=1,a,b,l;i<n;i++) {
33             scanf("%d%d%d",&a,&b,&l) , sum += l , ++a , ++b;
34             addedge(a,b,l) , addedge(b,a,l);
35         }
36         dep[mxp=1] = 0  , dfs(1,-1);
37         dep[mxp] = 0 , dfs(mxp,-1);
38         ( sum <<= 1 ) -= max( dep[mxp] - c , 0 );
39         printf("%d\n",sum);
40     }
41 }
42 
43 int main() {
44     while( scanf("%d%d%d",&n,&k,&c) == 3 ) {
45         if( k == 0 ) Case1::work();
46         else if( k == 1 ) Case2::work();
47     }
48     return 0;
49 }
View Code

55分暴力代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring> 
 4 #include<algorithm>
 5 #define debug cout
 6 using namespace std;
 7 const int maxn=2e3+1e2;
 8 const int inf=0x3f3f3f3f;
 9 
10 int s[maxn],t[maxn<<1],nxt[maxn<<1],l[maxn<<1],cnt;
11 int fa[maxn],dep[maxn],sou[maxn],dis[maxn],mxd[maxn],scd[maxn];
12 int pa,pb,pdis;
13 
14 inline void coredge(int from,int to,int len) {
15     t[++cnt] = to , l[cnt] = len , 
16     nxt[cnt] = s[from] , s[from] = cnt;
17 }
18 inline void doubledge(int from,int to,int len) {
19     coredge(from,to,len) , coredge(to,from,len);
20 }
21 
22 inline void pre(int pos) {
23     for(int at=s[pos];at;at=nxt[at]) if( t[at] != fa[pos] ) {
24         fa[t[at]] = pos , sou[t[at]] = at ,
25         dep[t[at]] = dep[pos] + 1 , pre(t[at]);
26     }
27 }
28 inline void dfs(int pos) {
29     mxd[pos] = pos , scd[pos] = pos , dis[pos] = 0;
30     for(int at=s[pos];at;at=nxt[at]) if( t[at] != fa[pos] ) {
31         dfs(t[at]) , dis[mxd[t[at]]] += l[at];
32         if( dis[mxd[t[at]]] >= dis[mxd[pos]] ) scd[pos] = mxd[pos] , mxd[pos] = mxd[t[at]];
33         else if( dis[mxd[t[at]]] > dis[scd[pos]] ) scd[pos] = mxd[t[at]];
34     }
35     if( dis[mxd[pos]] + dis[scd[pos]] > pdis ) {
36         pa = mxd[pos] , pb = scd[pos] , pdis = dis[mxd[pos]] + dis[scd[pos]];
37     }
38 }
39 inline void reval(int a,int b) {
40     while( a != b ) {
41         if( dep[a] > dep[b] ) l[sou[a]] = l[sou[a]^1] = -l[sou[a]] , a = fa[a];
42         else l[sou[b]] = l[sou[b]^1] = -l[sou[b]] , b = fa[b];
43     }
44 }
45 
46 inline void reset(int n) {
47     memset(s,0,sizeof(*s)*(n+1)) , cnt = 1;
48 }
49 
50 int main() {
51     static int n,k,c,sum;
52     while( scanf("%d%d%d",&n,&k,&c) == 3 ) {
53         reset(n) , sum = 0;
54         for(int i=1,a,b,l;i<n;i++) {
55             scanf("%d%d%d",&a,&b,&l) , sum += l << 1;
56             doubledge(++a,++b,l);
57         }
58         pre(1);
59         while( k ) {
60             pa = pb = -1 , pdis = -inf;
61             dfs(1);
62             if( pdis >= c ) sum -= pdis - c  , --k , reval(pa,pb);
63             else break;
64         }
65         printf("%d\n",sum);
66     }
67     return 0;
68 }
View Code

正解代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define debug cout
 6 using namespace std;
 7 const int maxn=2e4+1e2;
 8 
 9 struct Node {
10     int val,siz;
11     friend Node operator + (const Node &a,const Node &b) {
12         return (Node){a.val+b.val,a.siz+b.siz};
13     }
14     friend Node operator - (const Node &a,const Node &b) {
15         return (Node){a.val-b.val,a.siz-b.siz};
16     }
17     friend bool operator < (const Node &a,const Node &b) {
18         return a.val != b.val ? a.val < b.val : a.siz < b.siz;
19     }
20 }f[maxn][2],per;
21 
22 int s[maxn],t[maxn<<1],nxt[maxn<<1],l[maxn<<1],cnt;
23 int n,k,c,sum;
24 
25 inline void addedge(int from,int to,int len) {
26     t[++cnt] = to , l[cnt] = len ,
27     nxt[cnt] = s[from] , s[from] = cnt;
28 }
29 
30 inline void dfs(int pos,int fa) {
31     f[pos][0] = (Node){0,0} , f[pos][1] = per; // only access point pos by car .
32     for(int at=s[pos];at;at=nxt[at]) if( t[at] != fa ) {
33         dfs(t[at],pos);
34         const Node w = (Node){l[at],0};
35         const Node tp0 = std::min( f[pos][1] + f[t[at]][1] - per , f[pos][0] + f[t[at]][0] + w );
36         // access t[at] and other sons by car and walk back , access t[at] and other sons on foot and walk back .
37         const Node tp1 = std::min( f[pos][1] + f[t[at]][0] + w , f[pos][0] + f[t[at]][1] );
38         // access other t[at] on foot first and access other sons by car , access other sons on foot first and access t[at] by car .
39         f[pos][0] = std::min( tp0 , tp1 ) , f[pos][1] = std::min( tp0 + per , tp1 );
40     }
41 }
42 
43 inline Node calc(int cost) {
44     per = (Node){cost,1};
45     dfs(1,-1);
46     return f[1][0];
47 }
48 inline void bin() {
49     Node now;
50     if( ( now = calc(c) ).siz <= k ) return void(printf("%d\n",sum+now.val));
51     int ll = 0 , rr = 1e9 , mid;
52     while( rr > ll + 1 ) {
53         mid = ( ll + rr ) >> 1;
54         if( ( now = calc(mid) ).siz <= k ) rr = mid;
55         else ll = mid;
56     }
57     now = calc(rr);
58     printf("%d\n",sum+now.val-k*(rr-c));
59 }
60 
61 inline void init() {
62     memset(s,0,sizeof(int)*(n+1)) , sum = cnt = 0;
63 }
64 
65 int main() {
66     while( scanf("%d%d%d",&n,&k,&c) == 3 ) {
67         init();
68         for(int i=1,a,b,l;i<n;i++) {
69             scanf("%d%d%d",&a,&b,&l) , ++a , ++b , sum += l;
70             addedge(a,b,l) , addedge(b,a,l);
71         }
72         bin();
73     }
74     return 0;
75 }
View Code


T3:


我说这题修改了5次数据你信吗......
考场上看到这种情况根本不愿意写这题......
首先说明题意:一个插线板会在某时间段开始的时候被插入,在某时间段结束的时候被移除。
同时每个同学只能利用一个插线板,不能中途更换插线板。
这个同学利用的 插线板起始点 一定<= l ,所以我们可以枚举这个 插线板的起始点 。
考虑分块,我们可以预处理出 起始点小于等于每个块起点的某个时间 到 所有终点 的代价。
然后暴力计算 起始点 从 块左 到 用电起始点l 的代价。
对于第二个,我们可以主席树求和计算,而我orz了何中天大爷的可持久化块状数组写法。
毕竟这个东西查询修改O(sqrt),查询O(1),而这个题显然查询更多一些。
代码:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<set>
  6 #define debug cout
  7 using namespace std;
  8 const int maxn=1e5+1e2,blk=3e2;
  9 const int inf=0x3f3f3f3f;
 10 
 11 struct PersistentBlockedArray {
 12     int* p[maxn/blk+2];
 13     inline void update(int x) {
 14         const int bel = x / blk;
 15         int* t = new int[blk];
 16         if( p[bel] ) memcpy(t,p[bel],sizeof(int)*blk);
 17         else memset(t,0,sizeof(int)*blk);
 18         ++t[x%blk] , p[bel] = t;
 19     }
 20     inline int query(int x) {
 21         const int bel = x / blk;
 22         return p[bel] ? p[bel][x%blk] : 0;
 23     }
 24 }arr[maxn];
 25 
 26 struct Event {
 27     int tpe,tim,id;
 28     friend bool operator < (const Event &a,const Event &b) {
 29         return a.tim < b.tim;
 30     }
 31 }eve[maxn];
 32 int l[maxn],r[maxn],suc[maxn];
 33 int sum[maxn],pans[maxn/blk+2][maxn];
 34 int n,m,cnt;
 35 
 36 inline void getsuc() {
 37     set<int> s;
 38     for(int i=1;i<=n;i++) {
 39         if( !eve[i].tpe ) s.erase(eve[i].id);
 40         set<int>::iterator it = s.lower_bound(eve[i].id);
 41         if( it != s.end() ) suc[i] = *it;
 42         if( eve[i].tpe ) s.insert(eve[i].id);
 43     }
 44 }
 45 inline void prearr() {
 46     for(int i=1;i<=n;i++) {
 47         arr[i] = arr[i-1];
 48         if( !eve[i-1].tpe && suc[i-1] ) arr[i].update(suc[i-1]);
 49         if( eve[i].tpe && suc[i] ) arr[i].update(suc[i]);
 50     }
 51 }
 52 inline void getpans() {
 53     for(int j=1;j<=n;j+=blk) {
 54         memset(sum,0,sizeof(sum));
 55         for(int i=j;i<=n;i++) if( suc[i] ) ++sum[suc[i]];
 56         int miv = inf;
 57         for(int i=n;i>=j;i--) {
 58             const int id = eve[i].id , su = suc[i];
 59             if( !eve[i].tpe && l[id] <= j ) miv = min( miv , sum[id] );
 60             if( !eve[i].tpe && su ) {
 61                 --sum[su];
 62                 if( l[su] <= j ) miv = min( miv  , sum[su] );
 63             }
 64             pans[j/blk][i] = miv;
 65             if( eve[i].tpe && su ) {
 66                 --sum[su];
 67                 if( l[su] <= j ) miv = min( miv , sum[su] );
 68             }
 69         }
 70     }
 71 }
 72 
 73 inline int solve(int ll,int rr) {
 74     if( ll < 0 || rr > n || ll > rr ) cerr<<"illeagle argument !"<<endl,exit(0);
 75     const int bl = ( ll - 1 ) / blk;
 76     int ret = pans[bl][rr];
 77     for(int i=bl*blk+1;i<=ll;i++) {
 78         const int id = eve[i].id , su = suc[i];
 79         if( r[id] >= rr ) ret = min( ret , arr[rr].query(id) - arr[ll].query(id) );
 80         if( r[su] >= rr ) ret = min( ret , arr[rr].query(su) - arr[ll].query(su) );
 81     }
 82     return ret;
 83 }
 84 
 85 int main() {
 86     int lastans=0,tpe;
 87     scanf("%d%*d%d%d",&n,&m,&tpe);
 88     for(int i=1;i<=n;i++) {
 89         scanf("%d%d",l+i,r+i);
 90         eve[++cnt] = (Event){1,l[i],i} , eve[++cnt] = (Event){0,r[i],i};
 91     }
 92     sort(eve+1,eve+1+(n=cnt)) , n = cnt;
 93     getsuc() , prearr() ,
 94     getpans();
 95     for(int i=1,l,r;i<=m;i++) {
 96         scanf("%d%d",&l,&r);
 97         if( tpe ) l ^= lastans , r ^= lastans;
 98         lastans = solve(l,r);
 99         printf("%d\n",lastans!=inf?lastans:-1);
100         if( lastans == inf ) lastans = 0;
101     }
102     return 0;
103 }
View Code



关于本文密码:
KurenaiKisaragi
其实就是如月红啦......
准备把北京集训系列都改成这个密码的说......
(不要用"这个死肥宅又换老婆了"的眼神看着这里嘛)

posted @ 2018-03-22 09:08  Cmd2001  阅读(135)  评论(0编辑  收藏  举报