Codeforces Round #168 (Div. 1) 完全

地址:http://codeforces.com/contest/274

解题报告   http://codeforces.com/blog/entry/6759

赛后补完后都忘了总结。。。。。

A:找出一个最大的集合,使得不存在一个数是另一个数的k倍,将矛盾的数之间建边,会有很多条单链,可知有一半的数可以一起存在

http://codeforces.com/contest/274/submission/3166962

B:给你一棵树,每个点的权值有正有负,现在让你用最少的操作次数 , 让整棵树的权值为零。

一次操作是这样的: 一次只能给一个联通子集 +1 或 -1,这个连通子集必须要包括1这个点。

显然,以1为根,自底向上来做,把当前点变成0,父亲节点必须要做一样的操作,然后一直到根就可以了。

http://codeforces.com/contest/274/submission/3167398

C:有一百个圆心,每个圆不断的膨胀,最后变成一体,判断最后一个点消失的时间。

只有锐角三角形才需要判断,所以算法是n^3暴力枚举每个三角形,不过还需要判断两个直角三角形组成一个矩形的特殊情况



#include <math.h>
#include <cstdio>
#include <map>
#include <algorithm>
using namespace std;
const double eps = 1e-5;
struct point{double x,y;};
struct line{point a,b;};
point p[110],cen;
int n ;
double ans ;
double dis(point p1,point p2){
  return (p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y);
}
point intersection(line u,line v){
  point ret=u.a;
  double t=((u.a.x-v.a.x)*(v.a.y-v.b.y)-(u.a.y-v.a.y)*(v.a.x-v.b.x))
      /((u.a.x-u.b.x)*(v.a.y-v.b.y)-(u.a.y-u.b.y)*(v.a.x-v.b.x));
  ret.x+=(u.b.x-u.a.x)*t;
  ret.y+=(u.b.y-u.a.y)*t;
  return ret;
}
//外心
point circumcenter(point a,point b,point c){
  line u,v;
  u.a.x=(a.x+b.x)/2;
  u.a.y=(a.y+b.y)/2;
  u.b.x=u.a.x-a.y+b.y;
  u.b.y=u.a.y+a.x-b.x;
  v.a.x=(a.x+c.x)/2;
  v.a.y=(a.y+c.y)/2;
  v.b.x=v.a.x-a.y+c.y;
  v.b.y=v.a.y+a.x-c.x;
  return intersection(u,v);
}
void Init() {
  scanf("%d",&n);
  for(int i =0 ; i < n ; i++) scanf("%lf%lf",&p[i].x,&p[i].y);
}
void judge(int i,int j,int k,double dx)
{
     bool f = true;
     for(int kk = 0; kk < n; kk++){
          if(kk!=i && kk!=j && kk!=k) {
              double dd = dis(cen,p[kk]) ;
              if(dd - dx < -eps) { f = false;   break;}
          }
     }
     if(f) ans = max(ans,dx);
}
void Gao() {
  ans = -1;
 for(int i = 0; i < n; i++)  
 for(int j = i+1; j < n; j++) 
 for(int k = j+1; k < n; k++)
    {    
        double d[3];
        d[0] = dis(p[i],p[j]);
        d[1] = dis(p[i],p[k]); 
        d[2] = dis(p[j],p[k]);
        if(d[0] == d[1] + d[2]) 
        {
            int aim = -1;
            for(int l = 0; l < n; l++)
            {
              if(l == i || l==j || l==k) continue;
              double d1 = dis(p[i],p[l]);
              double d2 = dis(p[j],p[l]);
              if(d[0] == d1 + d2) aim = l;
            }
            if(aim!=-1) //长方形 
            {
               cen.x = (p[i].x+p[j].x) / 2;
               cen.y = (p[i].y+p[j].y) / 2;
               double dx = dis(cen,p[i]);
               judge(i,j,k,dx);
            }
        }
        sort(d,d+3);
        if(d[0]+d[1] <= d[2]) continue; 
        cen = circumcenter(p[i],p[j],p[k]);
        double dx = dis(cen,p[i]);
        judge(i,j,k,dx);
    }
   if(ans != -1) printf("%lf",sqrt(ans)); else puts("-1");
}
int main(){
  Init();
  Gao();
  return 0;
}


D:给你一个n*m的矩阵,问你是否存在一种方案,使得重新摆放每列的位置之后每行的元素是非递减的。

很容易想到一个拓扑模型,不过要建的边太多了貌似,方法是建一些虚拟节点。具体见这里

以下自然段转自http://blog.csdn.net/zucc_dianbei/article/details/8600058

把列之间的关系转化为图进行拓扑排序是很容易想到的一个突破口,关键在于如何建图。举一个只有一行的例子:1,2,3,4,5。点1比其他点都小,所以点1向其他4点各连一条边,点2往3,4,5连边,依次类推。。。这样正确但边数太多了。对于这个例子可以这样建图1->2->3->4->5 (每个点只向最小的比其大的点连边),边的数量大大减少。但如果是1,1,1,2,2,2这种数据又行不通了,所有的1都要向每个2连边,边的数量还是平方级别的。这里我们可以用添加虚拟节点的方法来解决,对于第2个例子,增加3个虚拟节点v1,v2,v3,v1向所有1连边,所有1向v2连边,v2向所有2连边,所有2向v3连边。这种方法保证边的数量是O(n*m)的

#include<queue>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 100010;
struct node {
    int num , id;
    bool operator < (const node&cmp) const {
        return num < cmp.num;
    }
}in[maxn];
vector<int> edge[maxn*2];
int ind[maxn*2];
int main()
{
    int n , m , k = 0;
    cin >> n >> m;
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < m; j++)
        {
            cin>>in[j].num;
            in[j].id = j;
        }
        sort(in,in+m);
        for(int j= 0; j < m; j++) if(in[j].num!=-1)
        {
            if(j==0 || in[j].num!=in[j-1].num) k++;
            edge[in[j].id].push_back(m+k);
            edge[m+k-1].push_back(in[j].id);
        }
        k++;
    }
    for(int i = 0; i < m+k; i++) for(int j = 0; j < edge[i].size(); j++) ++ind[edge[i][j]];
    queue<int> Q; vector<int> ans;
    for(int i = 0; i < m+k; i++) if(ind[i]==0) Q.push(i);
    while(!Q.empty())
    {
        int fr = Q.front(); Q.pop();
        if(fr < m) ans.push_back(fr);
        for(int i=0;i<edge[fr].size();i++) if((--ind[edge[fr][i]])==0) Q.push(edge[fr][i]);
    }
    if(ans.size()==m)
    {
        for(int i = 0; i < ans.size(); i++) cout<<ans[i]+1<<" ";
    }
    else cout<<"-1";
    return 0;
}

E题:可以将所有的斜条分类,\ 这种斜条的x-y是相等的,/ 这种斜条的x+y是相等的,所有就可以根据这个值将所有的障碍方格离散出来,每次发射的时候二分查找会碰到哪一个障碍格子,还有一个性质就是一个方格只会一一种方向进入,可以根据奇偶性来证明,\ / 这两种“斜”法是不可能进去同一个格子的,因为奇偶性不同,所以就不用担心某个格子会被这样经过两次,具体实现的细节需要想清楚。





#include <cstdio>
#include <cstring>
#include <set>
#include <string>
#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef pair<int,int> pii;
#define sqr(x) ((x)*(x))
#define PB push_back
#define MP make_pair
#define ins insert
#define X first
#define Y second
set<pii>::iterator it;
#define Tr(it, x) for(it = x.begin(); it!=x.end();it++)
#define SZ(x) x.size()
#define SORT(x) sort(x.begin(),x.end())
#define REP(i,n) for(int i=0;i<n;i++)
const int maxn = 200010;
const int mod = 1000000007;
vector<pii> dag1[maxn],dag2[maxn];
set<pii> Point; 
int n , m , sx ,sy;
long long ans ;
/*
   0  3
   2  1
*/
pii get_val(int x,int y)
{
    return MP(y-x+n+1,x+y);
}
bool judge(int x,int y,int dir)
{
    return ( (x+y == sx+sy) &&(dir==2||dir==3) || (x-y == sx-sy) && (dir==0||dir==1)) ;
}
int mp1[] = {3,2,1,0};
int mp2[] = {2,3,0,1};
int cnt;
void go(int x,int y,int dir)
{
    pii val = get_val(x,y);
    vector<pii> to;
    vector<pii>::iterator it;
    to = dir < 2 ? dag1[val.X] : dag2[val.Y];
    it = lower_bound(to.begin(),to.end(),MP(x,y));
    if(dir == 3) --it;
    if(dir == 0) --it;
    if(judge(x,y,dir)) if(sx > min(it->X,x) && sx < max(it->X,x))
    {
        ans += abs(sx - x); return ;
    }
    ans += abs(it->X-x);
    int dx = x < it->X ? -1 : 1;
    int dy = y < it->Y ? -1 : 1;
    pii t1 = MP(it->X + dx , it->Y);
    pii t2 = MP(it->X, it->Y + dy);
    bool flag1 = (Point.find(t1)!=Point.end());
    bool flag2 = (Point.find(t2)!=Point.end());
    if(flag1 == flag2)  {
        x = it->X + dx;
        y = it->Y + dy;
        dir ^= 1;
    }
    else if(flag1){
        x =  t2.X;
        y =  t2.Y;
        dir = mp1[dir] ;
    } 
    else{
        x = t1.X;
        y = t1.Y;
        dir = mp2[dir];
    }
    if(MP(x,y)==MP(sx,sy)) return ;
    go(x,y,dir);
}
int main()
{
    int  k , x , y , sdir;
    cin >> n >> m >> k;
    REP(i,k)
    {
        scanf("%d%d",&x,&y);
        Point.ins(MP(x,y));
    }
    REP(i,n+2) Point.ins(MP(i,0)),Point.ins(MP(i,m+1));
    REP(i,m+2) Point.ins(MP(0,i)),Point.ins(MP(n+1,i));
    Tr(it,Point)
    {
       pii val = get_val(it->X,it->Y);
       dag1[val.X].PB(*it);
       dag2[val.Y].PB(*it);
    }
    REP(i,n+m+3) SORT(dag1[i]),SORT(dag2[i]);
    string s;
    scanf("%d%d",&sx,&sy);
    cin>>s;
    if(s == "NE") sdir = 3;
    else if(s == "NW") sdir = 0;
    else if(s == "SE") sdir = 1;
    else sdir = 2;
    go(sx,sy,sdir);
    go(sx,sy,sdir^1);
    printf("%I64d\n",ans/2);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值