Codeforces Round #403 (Div. 2) B 二分或三分 D 2-sat E 思维,dfs

Codeforces Round #403 (Div. 2, based on Technocup 2017 Finals)

B. The Meeting Place Cannot Be Changed

题意:n个人,各自在点x[i],每个人最大速度v[i],可以往左右两个方向走。要使这n个人在同一个点相聚,求最小时间。

tags:可以直接三分位置,有个坑,eps取1e-7竟然会超时,取1e-6就过了;另外三分也不知道怎么搞出单调性的,貌似大家都是“显然”,23333。。可以二分时间求交集,但在求交集时有点技巧,不断比较出左界最大值a和右界最小值b,看最后是否有a<=b

// 二分
#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
typedef long long ll;
const int N = 60005;

int n, x[N], v[N];
bool check(double t)
{
    double minn=2000000005, maxn=0;
    rep(i,1,n) {
        minn=min(minn, x[i]+v[i]*t);
        maxn=max(maxn, x[i]-v[i]*t);
    }
    return maxn<=minn;
}
int main()
{
    scanf("%d", &n);
    rep(i,1,n) scanf("%d", &x[i]);
    rep(i,1,n) scanf("%d", &v[i]);
    double l=0, r=1000000005, ans;
    while(fabs(r-l)>1e-7) {
        double mid=(l+(r-l)/2);
        if(check(mid)) r=mid, ans=mid;
        else l=mid;
    }
    printf("%.12f\n", ans);

    return 0;
}
二分
// 三分
#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
typedef long long ll;
const int N = 60005;

int n;
double x[N], v[N];
double cal(double xi)
{
    double res=0;
    rep(i,1,n) res=max(res, fabs(xi-x[i])/v[i]);
    return res;
}
int main()
{
    scanf("%d", &n);
    rep(i,1,n) scanf("%lf", &x[i]);
    rep(i,1,n) scanf("%lf", &v[i]);
    double l=0, r=1000000005;
    while(fabs(r-l)>1e-6) {
        double m1=l+(r-l)/3, m2=r-(r-l)/3;
        double t1=cal(m1), t2=cal(m2);
        if(t1<t2) r=m2;
        else l=m1;
    }
    printf("%.12f\n", cal(l));

    return 0;
}
三分

D. Innokenty and a Football League

题意:每个团队有两个单词s1,s2,有两种取名字的方法:1、a=s1[0]+s1[1]+s1[2];2、a'=s1[0]+s1[1]+s2[0]。限制:每个团队名字不能相同,而且,如果一个团队以第二种方法取名a',其它团队就不能以第一种方法取名a。求是否可能给每个团队都取名。

tags:第一次写2-sat,强行码了一发。。这题还有用贪心的,但感觉好玄   

每个团队都只能在2种可能里选一种,而各个团队的可能又会互相影响,即裸的2判定性问题。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
typedef long long ll;
const int N = 200005;

int n, m, k, tot, head[N];
string s[3];
map<string, int >mp;
map<int , string>pm;
struct Club{int fir, sec;}a[N];
struct Edge{int to, next;}e[N];
void Addedge(int u, int v){e[++tot].to=v, e[tot].next=head[u], head[u]=tot;}

bool vis[N];
int Stack[N], top;
bool dfs(int u)
{
    if(vis[u^1]) return false;
    if(vis[u]) return true;
    vis[u]=true;
    Stack[top++]=u;
    for(int i=head[u]; i; i=e[i].next)
        if(dfs(e[i].to)==0) return false;
    return true;
}
bool Twosat(int n)     //n
{
    memset(vis,false,sizeof(vis));
    for(int i=0; i<n; i+=2) {       //i+=2
        if(vis[i] || vis[i^1]) continue;
        top=0;
        if(dfs(i)==0) {
            while(top) vis[Stack[--top]]=false; //记录路径
            if(dfs(i^1)==0) return false;   //如果矛盾返回flase
        }
    }
    return true;
}

int main()
{
    scanf("%d", &n);
    rep(i,1,n) {
        cin>>s[1]>>s[2];
        string s1=s[1].substr(0,3);
        string s2=s[1].substr(0,2)+s[2][0];
        if(mp[s1]==0) mp[s1]=++k, pm[k]=s1;
        if(mp[s2]==0) mp[s2]=++k, pm[k]=s2;
        a[i].fir=mp[s1], a[i].sec=mp[s2];
    }
    rep(i,1,n) rep(j,1,n) {     //加边,不太理解
        if(i==j) continue;
        if(a[i].fir==a[j].fir) Addedge(2*i-2, 2*j-1), Addedge(2*i-1, 2*j-1);
        if(a[i].fir==a[j].sec) Addedge(2*i-2, 2*j-2);
        if(a[i].sec==a[j].fir) Addedge(2*i-1, 2*j-1);
        if(a[i].sec==a[j].sec) Addedge(2*i-1, 2*j-2);
    }
    if(Twosat(2*n)) {           //2*n
        puts("YES");
        rep(i,0,2*n-1) if(vis[i]) {
            if((i+1)&1) cout<<pm[a[(i+2)/2].fir]<<endl;
            else cout<<pm[a[(i+1)/2].sec]<<endl;
        }
    } else {
        puts("NO");
    }

    return 0;
}
View Code

E. Underground Lab

题意:n个点m条无向边k个人,每个人每秒可以走过一条边。每个人最多可以经过 2n/k 个点(可重复),要使得最终n个点都至少被一个人遍历过,求每个人的路径走法。

tags:考思维,想不到啊mdzz

因为每个人最多可经过2n/k个点,k个人即最多可经过2n个点。这样的话,我们可以按dfs序搜一遍,把所有结点(包括dfs中重复的)都记录下来,再分配给这k个人即可。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a;i<=b;i++)
#define per(i,b,a) for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
typedef long long ll;
const int N = 200005;

int n, m, k, path[N<<1], cnt;  //路径要开双倍
bool vis[N];
vector<int >G[N];
void dfs(int u)
{
    vis[u]=1;
    path[++cnt]=u;
    for(auto v : G[u]) {
        if(vis[v]) continue;
        dfs(v);
        path[++cnt]=u;
    }
}
int main()
{
    scanf("%d %d %d", &n, &m, &k);
    int u, v;
    rep(i,1,m) {
        scanf("%d %d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1);
    int num=(2*n+k-1)/k, len;
    rep(i,1,k) {
        len=min(num, cnt);
        if(len==0) { printf("1 1\n"); continue; }
        printf("%d ", len);
        for(int i=0; i<len && cnt; i++, cnt--)
            printf("%d ", path[cnt]);
        puts("");
    }

    return 0;
}
View Code
posted @ 2017-03-08 00:23  v9fly  阅读(104)  评论(0编辑  收藏  举报