[Codeforces Gym][GCPC2018]

 


Acyclic_SD's First Training...

首先吐槽,这个队名真有那么难记吗...)

赛场上 solved 10 out of 13 赛后浩然帮我们队改过了K 第二天我补掉了了J

A题 “首先你要看出它是棵树” 接着就是个LCA求dep的事情.当然这题不是我写的,想来建图的过程还是有一点点码农的...队友很劲...btw...记得自己以前专门喜欢离线求Lca...但现在也没什么大必要就是了

B题 是个几何...几何...圆环内的两个点最短路径...大胆猜想作两条切线...又是队友实现的...nbnb

 C也是个签到题 有向无环图找任意两点间最长路径...应该是?...其实题目都应该好好读的...不论是跟榜的题还是没有人做的题...如果一场比赛下来连题目都没通读完的话...那确实可以怀疑一下自己的比赛状态了  

D题 珍贵又稀有的我可以1A的题(雾以后要增加1A率)... 确认了一下以后就单开了...应该是个大签到题 

E题 欧拉方法球素数+gcd然后就没了(这样想想这套题真的很良心 连数学题都是水水的) 队友wa到爆炸的原因是没有判断1是否为素数...我check的时候想到了这个问题来着...但是没有细读代码 

F题 貌似是建模以后看出来是fibonacci number就好了 + Be careful about monsters with power 1 

G题 哈哈哈哈哈啃了挺久的题解 但还是摸了 几何题 计算卫星与地球上某一点的距离 其中给出的条件不是很好转换 经度纬度...与赤道的夹角和交点...此时突然觉得heltion老师是无人能敌... 

 H题 依旧没看...原来队友切题的时候我都在纠结I题...应该是个比较暴力的数学...算出复杂度不会炸以后似乎就可以做了 

I题 莫名其妙变成了没人看得懂的阅读理解题 然后wa到爆炸..搁浅了很久...当然这个题意确实写的很不怎么样 

J题 拼图题 是个大模拟 想来应该贴一下代码 毕竟以后要一点点积累模拟的正确姿势...看着精妙的std确实可以使自己补题的效率迅速提高 x)  一开始数组开小了(因为每块拼图都有四个方向) 后来总算在qsc例会的时候改过了...需要注意的point: vector使用之前记得resize 否则至少在我本地会RE 然后多用queue 存两个位置的时候可以考虑xor嗯

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#define N 505050
#define ll long long
using namespace std;
int xmin=0,xmax=0,ymin=0,ymax=0;
int n,dx[10],dy[10],val[N*4],ch[10],b[N][5],x[N],y[N];
vector<vector<int> > res;
queue<int>q;bool mark[N];
void work(int *b){
    for(int i=1;i<=4;i++)b[i-1]=b[i]; b[4]=b[0];
}
int main(){
   // freopen("1.in","r",stdin);
    dx[1]=1;dx[2]=0;dx[3]=-1;dx[4]=0;
    dy[1]=0;dy[2]=1;dy[3]=0;dy[4]=-1;
    ch[1]=3;ch[2]=4;ch[3]=1;ch[4]=2;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=4;j++){
            cin>>b[i][j];
            val[b[i][j]]^=i;
        }
    }
    q.push(1);
    mark[1] = true;
    while(!q.empty()){
        int i=q.front();q.pop();
        xmin=min(xmin,x[i]);
        xmax=max(xmax,x[i]);
        ymin=min(ymin,y[i]);
        ymax=max(ymax,y[i]);
        for(int k=1;k<=4;k++){
            int e=b[i][k];
            if(e==0)continue;
            int j=val[e]^i;
            if(mark[j])continue;
            x[j]=x[i]+dx[k];
            y[j]=y[i]+dy[k];
            while(b[j][ch[k]]!=e)work(b[j]);
            q.push(j);
            mark[j]=true;
        }
    }
    
    for(int i=1;i<=n;i++)if(!mark[i]){
        printf("impossible\n");
        return 0;
    }
    int h=xmax-xmin+1,w=ymax-ymin+1;
    //printf("%d %d\n",h,w);
    //return 0;
    if((ll)h*(ll)w!=(ll)n){
        printf("impossible\n");
        return 0;
    }
    res.resize(h+10);
    for(int i=1;i<=h;i++)res[i].resize(w+10);
    for(int i=1;i<=n;i++)res[x[i]-xmin+1][y[i]-ymin+1]=i;
    for(int x=1;x<=h;x++)
     for(int y=1;y<=w;y++)
      if(res[x][y]==0){
          printf("impossible\n");
          return 0;
      }
    for(int x=1;x<=h;x++)
     for(int y=1;y<=w;y++)
      for(int k=1;k<=4;k++){
          int xx=x+dx[k],yy=y+dy[k];
          if(xx<1 || yy<1 || xx>h || yy>w){
              if(b[res[x][y]][k]!=0){
                  printf("impossible\n");
                  return 0;
              }
          }else{
              if(b[res[x][y]][k]!=b[res[xx][yy]][ch[k]] || b[res[x][y]][k]==0){
                  printf("impossible\n");
                  return 0;
              }
          }
      }
      printf("%d %d\n",h,w);
      for(int i=1;i<=h;i++){
          for(int j=1;j<=w;j++)
           printf("%d ",res[i][j]);
        printf("\n");
      }
    return 0;
}

 K题 01背包 将i个物品(正好)放入j的长度之中 使它们(j+10-g)/i最大 因为是i个而不是前i个 所以比原来多个一层循环

L题 类似于扫雷 幸好是想清楚以后写的 所以1A 

M题 并查集启发式合并...而我居然写了最小生成树+LCA...下面std CODE (直接把询问扔进set orz)

#include <bits/stdc++.h>
using namespace std;

#define FORN(i,n) for (int i = 0; i < (n); i++)

const int N = 1010, Q = 1e5 + 10;
int h[N*N], p[N*N];
set<int> s[N*N];
int res[Q];

int Find(int a) {
	if (a == p[a]) return a;
	return p[a] = Find(p[a]);
}

void Union(int a, int b, int h) {
	a = Find(a), b = Find(b);
	if (a == b) return;
	if (s[a].size() > s[b].size()) swap(a,b);
	
	for (int i: s[a]) {
		if (s[b].count(i)) {
			res[i] = h;
			s[b].erase(i);
		} else {
			s[b].insert(i);
		}
	}
	p[a] = b;
}

int I(int x, int y) { return x*N + y; }

const int dx[] = {1,-1,0,0}, dy[] = {0,0,1,-1};

int main() {
	int m, n, q;
	cin >> m >> n >> q;

	FORN(x,m) FORN(y,n) cin >> h[I(x,y)];
	
	FORN(i,q) {
		int x1, y1, x2, y2;
		cin >> x1 >> y1 >> x2 >> y2;
		x1--, y1--, x2--, y2--;
		s[I(x1,y1)].insert(i);
		s[I(x2,y2)].insert(i);
		
		if (x1 == x2 && y1 == y2) res[i] = h[I(x1,y1)];
	}
	
	vector<tuple<int,int,int>> v;

	iota(p,p+(N*N),0);
	FORN(x,m) FORN(y,n) v.emplace_back(h[I(x,y)],x,y);
	
	sort(begin(v),end(v));

	for (auto t: v) {
		int hh, x, y;
		tie(hh,x,y) = t;
		FORN(k,4) {
			int nx = x+dx[k], ny = y+dy[k];
			if (nx < 0 || nx >= m || ny < 0 || ny >= n) continue;
			if (h[I(nx,ny)] > h[I(x,y)]) continue;
			Union(I(x,y),I(nx,ny),hh);
		}
	}
	
	FORN(i,q) cout << res[i] << endl;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值