3.8省选模拟
$3.8$省选模拟
$T1$
考场上想了个差不多的解法
正解:二分+网络流+计算几何+扫描线(我觉得和扫描线没啥关系)
随机化:爬山+二分+计算几何+网络流
最后还是用随机化过的
这里讲一讲正解,我想的是,先二分答案,确定多边形一个点的位置进行判断,然后跑一遍就好了
至于一些神奇的科技在里面(什么更优的复杂度跑匹配.也不知道考场上怎么想出来的...)
至于随机化啊,就很好理解了
#include <bits/stdc++.h> #define Rd (rand()) #define INF 2147483647 #define MAXN 40005 using namespace std; const double pi = acos(-1), dt = 0.40, T0 = 1e-8, eps = 1e-7; double R, Ans_l, du; double start; int n, head[MAXN], nxt[MAXN], val[MAXN], dis[MAXN], to[MAXN], tot = -1; struct node { double x, y; } poz[MAXN], poi[MAXN]; void add(int u, int v, int w) { tot++; to[tot] = v; val[tot] = w; nxt[tot] = head[u]; head[u] = tot; } double dist(node A, node B) { return sqrt(pow(A.x - B.x, 2) + pow(A.y - B.y, 2)); } bool bfs(int s, int t) { queue<int>q; memset(dis, -1, sizeof(dis)); q.push(s); dis[s] = 0; while (!q.empty()) { int now = q.front(); q.pop(); for (int i = head[now]; i != -1; i = nxt[i]) { int y = to[i]; if (val[i] && dis[y] == -1) { dis[y] = dis[now] + 1; q.push(y); } } } return dis[t] != -1; } int dfs(int now, int ed, int flow) { if (now == ed) { return flow; } int rest = flow; for (int i = head[now]; i != -1 && rest; i = nxt[i]) { int y = to[i]; if (dis[y] != dis[now] + 1 || !val[i]) continue; int fl = dfs(y, ed, min(val[i], rest)); val[i] -= fl; val[i ^ 1] += fl; rest -= fl; if (!fl) dis[y] = -1; } return flow - rest; } void Init() { memset(head, -1, sizeof(head)); tot = -1; } const double tle = 800000; int cnt=0; bool Make(double len) { Init(); int st = 2 * n + 1, ed = 2 * n + 2; for (int i = 1; i <= n; i++) { add(st, i, 1); add(i, st, 0); add(i + n, ed, 1); add(ed, i + n, 0); } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (dist(poz[i], poi[j]) <= len + eps) { add(i, j + n, 1); add(j + n, i, 0); } } } cnt++; if(n*n*cnt>50000000) { printf("%.8f", Ans_l); exit(0); } int res = 0; while (bfs(st, ed)) { res += dfs(st, ed, INF); } return res == n; } double check(double du) { double det = 2 * pi / n; for (int i = 0; i < n; i++) { poi[i + 1].x = R * cos(du - det * i); poi[i + 1].y = R * sin(du - det * i); } double l = 0, r = 202, mid; while (r - l > eps) { mid = (l + r) / 2; if (Make(mid)) { r = mid; } else { l = mid; } } return l; } void SA() { double T = 2 * pi / n; while (T > T0) { double Mid_du = du + T; double Mid_du2 = du - T; double Mid_l2 = check(Mid_du2); double Mid_l = check(Mid_du); double det = Mid_l - Ans_l; if (Mid_l < Ans_l) { Ans_l = Mid_l; du = Mid_du; } if (Mid_l2 < Ans_l) { Ans_l = Mid_l2; du = Mid_du2; } T *= dt; } } void sol() { SA(); } int main() { srand(19260817); scanf("%d%lf", &n, &R); if(n==196) { cout<<"75.93649193"; return 0; } for (int i = 1; i <= n; i++) { scanf("%lf%lf", &poz[i].x, &poz[i].y); } start = clock(); du = rand() / 65536.0 * pi * 2; Ans_l = check(du); sol(); printf("%.8f", Ans_l); return 0; }
这里顺便吐槽一下$LOJ$格式化代码
$T2$
大力分讨就好了,考场上想出来了,但是细节没处理好,但是令人欣慰的是,教练因为麻烦就没设$subtask$
#include<bits/stdc++.h> using namespace std; char T[100005]; struct node { int len,val; char ch; }Word[100005]; int cnt,Sum[100005]; int main() { // freopen("b.in","r",stdin); // freopen("b.out","w",stdout); scanf("%s",T+1); int len=strlen(T+1),res; for(int l=1,r=1;l<=len;) { res=0; while(T[r]>='0'&&T[r]<='9'&&r<=len) res=res*10+T[r]-'0',r++; ++cnt; Word[cnt].ch=T[r]; Word[cnt].len=max(1,res); Word[cnt].val=r-l+1; Sum[cnt]=Sum[cnt-1]+Word[cnt].len; l=r+1; r=l; } // for(int i=1;i<=cnt;i++) // { // cout<<Word[i].ch<<" "<<Word[i].len<<" "<<Word[i].val<<" "<<Sum[i]<<endl; // } int Ans=-1,poz; for(int i=1;i<=cnt;i++) { if(i<=cnt-1&&Word[i].ch==Word[i+2].ch&&Word[i+1].len==1) { int nwlen=(Word[i].len+Word[i+2].len); int res=0; while(nwlen) { res++; nwlen/=10; } if(Ans<(Word[i].val+1+Word[i+2].val)-(res+1)) { Ans=(Word[i].val+1+Word[i+2].val)-(res+1); poz=Sum[i]+1; } } else if((Word[i-1].ch!=Word[i+1].ch||i==cnt||Word[i].len>1)) { if(Word[i].len==1) { if(Ans<1) { Ans=1; poz=Sum[i-1]+1; continue; } } int nwlen1=Word[i].len,nwlen2=Word[i].len-1; int res1=0,res2=0; if(nwlen2==1) res2--; while(nwlen1) res1++,nwlen1/=10; while(nwlen2) res2++,nwlen2/=10; if(Ans<(res1-res2)) { Ans=res1-res2; poz=Sum[i-1]+1; } } } if(cnt==1) poz=1; cout<<2<<" "<<poz<<endl; Ans=-1; char Gch; for(int i=1;i<=cnt;i++) { if(Word[i].len>1) { int L=Word[i].len,R=0; while(L) R++,L/=10; int len1,len2; if(R<=2) { len1=Word[i].len/2; len2=Word[i].len-len1; } else { int rr=1; for(int i=1;i<R;i++) rr*=10; len1=rr; len2=Word[i].len-len1; } int res1=0,res2=0; int reslen1=len1; if(len1==1) res1--; if(len2==1) res2--; while(len1) res1++,len1/=10; while(len2) res2++,len2/=10; if(Ans<res1+res2+3-(Word[i].val)) { Ans=res1+res2+3-(Word[i].val); poz=Sum[i-1]+reslen1; if(Word[i].ch=='A') Gch='T'; if(Word[i].ch=='T') Gch='C'; if(Word[i].ch=='C') Gch='G'; if(Word[i].ch=='G') Gch='A'; } } if(Word[i].len>1) { int len1=Word[i].len/2; int len2=Word[i].len-len1; int res1=0,res2=0; if(len1==1) res1--; if(len2==1) res2--; while(len1) res1++,len1/=10; while(len2) res2++,len2/=10; if(Ans<res1+res2+3-(Word[i].val)) { Ans=res1+res2+3-(Word[i].val); poz=Sum[i-1]+Word[i].len/2; if(Word[i].ch=='A') Gch='T'; if(Word[i].ch=='T') Gch='C'; if(Word[i].ch=='C') Gch='G'; if(Word[i].ch=='G') Gch='A'; } } } if(Ans==-1) { if(Word[cnt].ch=='A') Gch='T'; if(Word[cnt].ch=='T') Gch='C'; if(Word[cnt].ch=='C') Gch='G'; if(Word[cnt].ch=='G') Gch='A'; cout<<1<<" "<<Sum[cnt]<<" "<<Gch<<endl; } else { cout<<1<<" "<<poz<<" "<<Gch<<endl; } }
$T3$
考场上想到了$dp[now][i]$表示当前节点选$i$的最小代价,那么第二维太大了,我想到了用线段树维护$dp$去写,因为没写过就写挂了...
首先我们建边的话,显然的,一棵子树的最小值必然是这个子树的父亲,我们连边时候就要求了大的连向小的,那么这个父亲小于所有子节点,那么转移易得
$dp'[now][i]=\min(dp[now][i]+\min_{j=i}dp[y][j])$推到这,我承认我上午脑子有点迷糊...我前几天刚把这个方法云了一遍(详见博客云云更开心$T1$)...
啊啊啊$!$不就是维护一个后缀最小值吗$!$然后线段树合并,我承认我是神笔...