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$)...

啊啊啊$!$不就是维护一个后缀最小值吗$!$然后线段树合并,我承认我是神笔...

 

posted @ 2022-03-08 18:32  Authentic_k  阅读(30)  评论(0编辑  收藏  举报