动态题目问题,我们需要自动AC机......
T1:
看到划掉的那部分了吗,没错,考试中途改题面了......
然而我只会写15分爆搜......
计算几何+构造,不改了,爆搜滚粗了......
以下为官方题解:
15分爆搜代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }
标程代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }
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分代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }
55分暴力代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }
正解代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }
T3:
我说这题修改了5次数据你信吗......
考场上看到这种情况根本不愿意写这题......
首先说明题意:一个插线板会在某时间段开始的时候被插入,在某时间段结束的时候被移除。
同时每个同学只能利用一个插线板,不能中途更换插线板。
这个同学利用的 插线板起始点 一定<= l ,所以我们可以枚举这个 插线板的起始点 。
考虑分块,我们可以预处理出 起始点小于等于每个块起点的某个时间 到 所有终点 的代价。
然后暴力计算 起始点 从 块左 到 用电起始点l 的代价。
对于第二个,我们可以主席树求和计算,而我orz了何中天大爷的可持久化块状数组写法。
毕竟这个东西查询修改O(sqrt),查询O(1),而这个题显然查询更多一些。
代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }
关于本文密码:
KurenaiKisaragi
其实就是如月红啦......
准备把北京集训系列都改成这个密码的说......
(不要用"这个死肥宅又换老婆了"的眼神看着这里嘛)