ACM-ICPC 2018 沈阳赛区网络预赛(待更新)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Lfhase/article/details/82696774

ACM-ICPC 2018 沈阳赛区网络预赛(待更新)


E. The cake is a lie (最小圆覆盖)

题目链接
题面:

(骗搜索)
You promised your girlfriend a rounded cake with at least SS strawberries.

But something goes wrong, you don’t have the final cake but an infinite plane with NN strawberries on it. And you need to draw a circle which contains strawberries completely to make the cake by yourself.

To simplify the problem, the strawberries are represented as circles in 22D plane. Every strawberry has its center (X_i, Y_i)(X
i
​ ,Y
i
​ ) and the same radius RR.

The cost of your cake is the radius of the circle you draw. So you want to know the minimal cost of the cake, or if you can’t give your girlfriend such a cake.

Input
The input file contains several test cases.

The first line of the file is an integer TT, giving the number of test cases. (T\leq20)(T≤20)

For each test case, there are two integers NN and SS in the first line, denoting the number of total strawberries and the number of strawberries on cake as you promised. (1\leq N, S \leq 300)(1≤N,S≤300)

In the following NN lines, each line contains two integers X_i, Y_iX
i
​ ,Y
i
​ , denoting the coordinates of the strawberry center. (0\leq X_i, Y_i\leq 10000)(0≤X
i
​ ,Y
i
​ ≤10000)

The next line contains one integer RR, denoting the radius of all strawberries. (1\leq R \leq 10000)(1≤R≤10000)

It’s guaranteed that sum of NN is smaller than 10001000.

Output
Output the minimal cost correct to four decimal places if you can make a rounded cake with at least SS strawberries, or “The cake is a lie.” if not.

样例输入 复制
2
5 3
0 0
0 2
2 0
0 0
1 1
1
1 2
0 0
1
样例输出 复制
1.7071
The cake is a lie.

题意:

给定N个半径为D的圆,要你画一个大圆能覆盖至少S个这样的圆,问满足条件的最小圆半径。

思路:

二分圆半径即可,之后用最小圆覆盖验证。

AC代码:
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <vector>
#include <stack>
#include <bitset>

using namespace std;

#define FSIO  ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define DEBUG(a)   cout<<"DEBUG: "<<(a)<<endl;
#define ll long long
#define ld long double
#define pb push_back
#define mp make_pair
#define X  first
#define Y  second
#define REP(i,st,ed)    for(int i=st;i<=ed;++i)
#define IREP(i,st,ed)   for(int i=ed;i>=st;--i)
#define TCASE(T)    cin>>T;while(T--)


const int MAXN = 1005;
const ll MOD = 1e9+7 ;
const ll INF = 1e16+7;

int _;

using namespace std;



const double CGINF = 1e20;
const double EPS = 1e-8;
const double PI = acos(-1.0);

int sgn(double x)
{
    if(fabs(x) < EPS) return 0;
    if(x < 0) return -1;
    return 1;
}

struct Point{
    double x, y;
    Point(){};
    Point(double _x, double _y): x(_x), y(_y){}

    void read(){
        scanf("%lf%lf",&x,&y);
    }

    void output(){
        printf("%.2f %.2f\n",x,y);
    }

    bool operator == (Point& b) const{
        return (sgn(x-b.x) == 0) && (sgn(y-b.y) == 0);
    }

    bool operator < (Point& b) const{
        return sgn(x-b.x)==0?sgn(y-b.y)<0:x<b.x;
    }

    Point operator + (Point b) const{
        return Point(x+b.x, y+b.y);
    }

    Point operator - (const Point& b) const{
        return Point(x-b.x, y-b.y);
    }
    // cross product
    double operator ^ (const Point& b) const{
        return x*b.y - y*b.x;
    }
    // point prodct
    double operator * (const Point& b) const{
        return x*b.x + y*b.y;
    }

    double len(){
        return hypot(x, y);
    }

    double len2(){
        return x*x + y*y;
    }

    double dis(Point & b){
        return hypot(x-b.x, y-b.y);
    }
};


struct PolarAngle
{
    double angle;
    bool flag;
} pa[MAXN];

bool operator<(const PolarAngle &a, const PolarAngle &other)
{
    return a.angle < other.angle;
}

Point pp[MAXN];
int N;


int solve(double r)
{
    int res = 1;
    REP(i,1,N)
    {
        int m = 0;
        double dd;
        REP(j,1,N)
        {
            dd=pp[i].dis(pp[j]);
            if(i!=j&&dd<=2.0*r)
            {
                double phi = acos(dd/(2.0*r));
                double theta = atan2(pp[j].y-pp[i].y,pp[j].x-pp[i].x);
                if(theta<0) theta+=2*PI;
                pa[++m].angle = theta - phi+2*PI;
                pa[m].flag = 1;
                pa[++m].angle = theta + phi+2*PI;
                pa[m].flag = 0;
            }
        }
        sort(pa+1,pa+m+1);
        for(int sum=1,j=1;j<=m;++j)
        {
            if(pa[j].flag)  ++sum;
            else    --sum;
            res = max(res,sum);
        }
    }
    return res;
}


int main()
{
    //freopen("test.in","r",stdin);
    //freopen("test.in","w+",stdout);
    TCASE(_)
    {
        int S;
        scanf("%d%d",&N,&S);
        REP(i,1,N)  pp[i].read();
        double dd;
        scanf("%lf",&dd);
        double minn = 0;
        double maxn = 20000;
        double mid;
        if(S>N)
        {
            printf("The cake is a lie.\n");
            continue;
        }
        while(fabs(maxn-minn)>EPS)
        {
            mid=(minn+maxn)/2.0;
            if(solve(mid)>=S)   maxn=mid;
            else    minn=mid;
        }
        printf("%.4f\n",minn+dd);
    }
    return 0;
}

F. Fantastic Graph (有上下界的网络流)

题目链接
题面:

(骗搜索)
“Oh, There is a bipartite graph.”“Make it Fantastic.”

X wants to check whether a bipartite graph is a fantastic graph. He has two fantastic numbers, and he wants to let all the degrees to between the two boundaries. You can pick up several edges from the current graph and try to make the degrees of every point to between the two boundaries. If you pick one edge, the degrees of two end points will both increase by one. Can you help X to check whether it is possible to fix the graph?

Input
There are at most 3030 test cases.

For each test case,The first line contains three integers NN the number of left part graph vertices, MM the number of right part graph vertices, and KK the number of edges ( 1 \le N \le 20001≤N≤2000,0 \le M \le 20000≤M≤2000,0 \le K \le 60000≤K≤6000 ). Vertices are numbered from 11 to NN.

The second line contains two numbers L, RL,R (0 \le L \le R \le 300)(0≤L≤R≤300). The two fantastic numbers.

Then KK lines follows, each line containing two numbers UU, VV (1 \le U \le N,1 \le V \le M)(1≤U≤N,1≤V≤M). It shows that there is a directed edge from UU-th spot to VV-th spot.

Note. There may be multiple edges between two vertices.

Output
One line containing a sentence. Begin with the case number. If it is possible to pick some edges to make the graph fantastic, output “Yes” (without quote), else output “No” (without quote).

样例输入 复制
3 3 7
2 3
1 2
2 3
1 3
3 2
3 3
2 1
2 1
3 3 7
3 4
1 2
2 3
1 3
3 2
3 3
2 1
2 1
样例输出 复制
Case 1: Yes
Case 2: No

题意:

给一个二部图,选择一个它边集的子集,使得每个顶点度数在[L,R]之间,判断是否可行。

思路:

转化为有上下界网络流。
添加一个超级原点往左部添加上下界为[L,R]的边,原二部图中边上下界都为1,右部连向一个超级汇点,每条边上下界为[L,R]。这样只需要求解每条从源点出发的边是否有流量,每条到达汇点的边是否有流量即可。
再次转化,添加一个新源点X,新汇点Y,原来每条上下界为[L,R]的边出发点都连向X,到达点都从Y引边到源到达点,最后从T添加一条容量为INF的边向S。最后判断最大流是否为顶点数*L即可。

AC代码:

const int MAXN = 100005;
const int MOD = 1e9+7 ;
const int INF = 1e9+7;

int _;

using namespace std;


struct Edge{
    int to, cap, next;
}edge[MAXN];

int adj[MAXN], cntE;
int dis[MAXN], cur[MAXN], pre[MAXN], num[MAXN];
int source, sink, nv;
int N, M;

void init()
{
    memset(adj,-1,sizeof(adj));
    cntE = 0;
}

void add_edge(int from, int to, int cap)
{
    edge[cntE].to = to;
    edge[cntE].cap = cap;
    edge[cntE].next = adj[from];
    adj[from] = cntE++;

    edge[cntE].to = from;
    edge[cntE].cap = 0;
    edge[cntE].next = adj[to];
    adj[to] = cntE++;
}

void rev_bfs()
{
    memset(num,0,sizeof(num));
    memset(dis,-1,sizeof(dis));
    dis[sink] = 0;
    num[0] = 1;
    queue<int> qqq;
    qqq.push(sink);
    while(!qqq.empty())
    {
        int u = qqq.front();
        qqq.pop();
        for(int i=adj[u];~i;i=edge[i].next)
        {
            int v = edge[i].to;
            if(~dis[v]) continue;
            dis[v] = dis[u] + 1;
            qqq.push(v);
            num[dis[v]]++;
        }
    }
}


int ISAP(int s, int t)
{
    source = s;
    sink = t;
    nv = t+1;
    memcpy(cur,adj,sizeof(adj));
    rev_bfs();
    int flow = 0;
    int u = pre[source] = source;
    while(dis[sink]<nv)
    {
        if(u==sink)
        {
            int f = INF;
            int neck;
            for(int i=source;i!=sink;i=edge[cur[i]].to)
            {
                if(f>edge[cur[i]].cap)
                {
                    f = edge[cur[i]].cap;
                    neck = i;
                }
            }
            for(int i=source;i!=sink;i=edge[cur[i]].to)
            {
                edge[cur[i]].cap -= f;
                edge[cur[i]^1].cap += f;
            }
            flow += f;
            u = neck;
        }
        int i;
        for(i=cur[u];~i;i=edge[i].next)
        {
            if(dis[edge[i].to]+1 == dis[u] && edge[i].cap)
                break;
        }
        if(~i)
        {
            cur[u] = i;
            pre[edge[i].to] = u;
            u = edge[i].to;
        }
        else
        {
            if((--num[dis[u]])==0)  break;
            int cnt = nv;
            for(i=adj[u];~i;i=edge[i].next)
            {
                if(edge[i].cap&&cnt>dis[edge[i].to])
                {
                    cur[u] = i;
                    cnt = dis[edge[i].to];
                }
            }
            dis[u] = cnt + 1;
            num[dis[u]]++;
            u = pre[u];
        }
    }
    return flow;
}


int main()
{
    //freopen("input","r",stdin);
    //freopen("output","w+",stdout);
    int K;
    int u, v, L, R;
    int tttt = 0;
    while(scanf("%d%d%d",&N,&M,&K)!=EOF)
    {
        scanf("%d%d",&L,&R);
        init();
        for(int i=1;i<=K;++i)
        {
            scanf("%d%d",&u,&v);
            add_edge(u,v+N,1);
        }
        add_edge(N+M+1,N+M+3,N*L);
        add_edge(N+M+2,N+M+1,INF);
        add_edge(N+M+4,N+M+2,M*L);
        for(int i=1;i<=N;++i)   add_edge(N+M+4,i,L);
        for(int i=1;i<=M;++i)   add_edge(N+i,N+M+3,L);
        int ans = ISAP(N+M+4,N+M+3);
        //printf("%d\n",ans);
        printf("Case %d: ",++tttt);
        if(ans==(N+M)*L)    printf("Yes\n");
        else    printf("No\n");
    }
    return 0;
}

G. Spare Trie (莫比乌斯反演+优化)

题目链接
题意:

求解如题中所示函数。

思路:

莫比乌斯反演。
反演过程
最后函数右部可以用公式O(1)时间求出。
左部可以先求出1e6的莫比乌斯函数值,剩下部分可以在有限步数下求解。

AC代码:

const double eps = 1e-8;

const int MAXN = 1000005;

bool check[MAXN];
int prime[MAXN];
int mu[MAXN];

void Moblus()
{
    memset(check,0,sizeof(check));
    mu[1] = 1;
    int tot = 0;
    for(int i=2;i<MAXN;++i)
    {
        if(!check[i])
        {
            prime[++tot] = i;
            mu[i] = -1;
        }
        for(int j=1;j<=tot;++j)
        {
            if(i*prime[j]>MAXN) break;
            check[i*prime[j]] = 1;
            if(i%prime[j]==0)
            {
                mu[i*prime[j]] = 0;
                break;
            }
            else    mu[i*prime[j]] = -mu[i];
        }
    }
}

int getmu(int n) {
    int cnt = 0;
    for(int i = 2; i * i <= n; ++i) {
        if(n%i == 0) {
            n /= i;
            ++cnt;
            if(n%i == 0) return 0;
        }
    }
    if(n > 1) ++cnt;
    return (cnt & 1) ? -1 : 1;
}

const LL mod = 1e9+7;

LL inv6;
LL inv2;

LL powermod(LL n,LL k, LL MOD)
{
    LL ans=1;
    while(k)
    {
        if(k&1)
        {
            ans=((ans%MOD)*(n%MOD))%MOD;
        }
        n=((n%MOD)*(n%MOD))%MOD;
        k>>=1;
    }
    return ans;
}

LL cal(LL n, LL d){
    n = n/d;
    return (1LL*d*d%mod*n%mod*(n+1LL)%mod*(2LL*n%mod+1LL)%mod*inv6%mod + (d*n%mod*(n+1LL)%mod*inv2%mod))%mod;
}

int main()
{
    inv2 = powermod(2, mod-2, mod);
    inv6 = powermod(6, mod-2, mod);
    Moblus();
    LL n, m;
    while(~scanf("%lld%lld", &n, &m)){
        int sq = sqrt(m);
        LL ans = 0;
        for(int i = 1; i <= sq; ++i){
            if(m%i == 0){
                ans = (ans + mu[i]*cal(n, i)%mod + mod)%mod;
                if(i*i == m){
                    break;
                }
                int divi = m/i;
                if(divi >= MAXN-5){
                    int muu = getmu(divi);
                    ans = (ans + muu*cal(n, divi)%mod + mod)%mod;
                }else{
                    ans = (ans + mu[divi]*cal(n, divi)%mod + mod)%mod;
                }
            }

        }
        printf("%lld\n", ans);
    }
    return 0;
}

J. Ka Chang (DFS序+时间戳+树状数组)

题目链接
题意:

处理题中描述两种询问。

思路:

对于询问2,显然可以用DFS序+树状数组/线段树求解。
对于询问1,使用分块的思想,对于 s q r t ( N ) 以下的直接处理,其他的则使用标记的方法,每次用子树对应深度顶点数*标记累加贡献。
如何解决某子树对应深度顶点数?DFS序时的时间戳。

AC代码:

const int MAXN = 100005;
const ll MOD = 1e9+7 ;
const ll INF = 1e16+7;

int _;

using namespace std;


ll tree[MAXN];
int N;

#define lowbit(x) (x&(-x))

void update(int pos, ll val)
{
    while(pos <= N)
    {
        tree[pos] += val;
        pos += lowbit(pos);
    }
}

ll query(int pos)
{
    ll res = 0;
    while(pos > 0)
    {
        res += tree[pos];
        pos -= lowbit(pos);
    }
    return res;
}


int head[MAXN];
int curedgeno;

struct Edge{
    int u, v;
    int next;
    Edge(int u=0, int v=0, int next=-1): u(u), v(v), next(next) {}
}edge[MAXN<<1];

void init()
{
    memset(head,-1,sizeof(head));
    curedgeno = 0;
}

void add_edge(int u, int v)
{
    ++curedgeno;
    edge[curedgeno] = Edge(u,v);
    edge[curedgeno].next = head[u];
    head[u] = curedgeno;
}

int in_time[MAXN], out_time[MAXN];
int tot;
vector<int> dd[MAXN];
int depth;
void dfs(int u, int fa, int dep)
{
    depth = max(depth,dep);
    in_time[u] = ++tot;
    dd[dep].pb(in_time[u]);
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v = edge[i].v;
        if(v!=fa)   dfs(v,u,dep+1);
    }
    out_time[u] = tot;

}

vector<int> larget;
ll store[MAXN];

int main()
{
    //freopen("test.in","r",stdin);
    //freopen("test.in","w+",stdout);
    int Q;
    while(scanf("%d%d",&N,&Q)!=EOF)
    {
        init();
        int u, v;
        REP(i,1,N-1)
        {
            scanf("%d%d",&u,&v);
            add_edge(u,v);
            add_edge(v,u);
        }
        tot = 0;
        REP(i,0,N)  dd[i].clear();
        larget.clear();
        depth = 0;
        dfs(1,-1,0);
        int block = sqrt(N);
        for(int i=0;i<=depth;++i)
            if(dd[i].size()>block)
                larget.pb(i);
        memset(tree,0,sizeof(tree));
        memset(store,0,sizeof(store));
        int op;
        ll val;
        while(Q--)
        {
            scanf("%d",&op);
            if(op==1)
            {
                scanf("%d%lld",&u,&val);
                if(dd[u].size()>block)  store[u]+=val;
                else
                {
                    for(int i=0;i<dd[u].size();++i)
                        update(dd[u][i],val);
                }
            }
            else
            {
                scanf("%d",&u);
                ll res = query(out_time[u])-query(in_time[u]-1);
                for(int i=0;i<larget.size();++i)
                {
                    res += 1LL*(upper_bound(dd[larget[i]].begin(),dd[larget[i]].end(),out_time[u])-
                                lower_bound(dd[larget[i]].begin(),dd[larget[i]].end(),in_time[u]))*store[larget[i]];
                }
                printf("%lld\n",res);
            }
        }
    }
    return 0;
}

K. Supereme Number(规律)

题目链接
题面:

冰冷签到题。

题意:

冰冷签到题。

思路:

冰冷签到题。

AC代码:

const int MAXN = 1005;
const int MOD = 998244353 ;
const int INF = 1e9+7;

int _;

using namespace std;

vector<int> num{1,2,3,5,7,11,13,17,23,31,37,53,73,71,113,131,311,137,173,317,713,371,731};
int mk[105];
string N;

int judge(int x)
{
    for(int i=2;i*i<=x;++i)
        if(x%i==0)  return 0;
    return 1;
}

void init()
{
    memset(mk,0,sizeof(mk));
    for(int i=0;i<num.size();++i)
        mk[i] = judge(num[i]);
}


int change()
{
    int res = 0;
    int base = 1;
    for(int i=N.length()-1;i>=0;--i)
    {
        res += base*(N[i]-'0');
        base*=10;
    }
    return res;
}



int main()
{
    //freopen("input","r",stdin);
    //freopen("output","w+",stdout);
    sort(num.begin(),num.end());
    init();
    int ans;
    cin>>_;
    for(int t=1;t<=_;++t)
    {
        cin>>N;
        if(N.length()>=4)   ans = 317;
        else
        {
            int tmp = change();
            for(int i=num.size()-1;i>=0;--i)
            {
                if(mk[i]&&num[i]<=tmp)
                {
                    ans = num[i];
                    break;
                }
            }
        }
        printf("Case #%d: %d\n",t,ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Lfhase/article/details/82696774