8.11 18级杭电多校第七场

比赛过程

  目前打的最顺的一场,曹老师带飞,太顶了。

题解

1007

题意

  二维平面有若干点,起点标号为1,从起点出,每次操作可以跳转到没有访问过的点,且跳转的步长要求严格递增,问先手的输赢情况。

解法

  这题当时看了好久好迷,后来曹老师合理推测加假设摸出了规律就过了,看题解真的是相当好理解,就是每次取出当前点集的最长边,把能构成最长边的点从原点集里面删去,然后单独归为一个集合,如此不断重复直到将原集合分成若干个集合,如果只剩下起始点没有被分入一个集合,那么必败,反之必胜,因为我每次都可以走当前集合的最长边,然后我的对手要么走不了了,要么只能走到下一个集合,因此我每一步都保证在集合内走,但我的对手每一步必须跨越集合,因此必胜。

代码
const int maxn = 5e6 + 5;
struct ac{
  int x, y;
  ll dis;
}a[maxn];
P node[maxn];
int vis[2005];
ll getdis(P x,P y){
  return (x.first - y.first) * (x.first - y.first) + (x.second - y.second) * (x.second - y.second);
}
bool cmp(ac a,ac b){
  return a.dis > b.dis;
}
int main(){
  IO;
  int T;
  cin >> T;
  while(T--){
    int n;
    cin >> n;
    int ls = 1;
    FOR(i,1,n){
      int x, y;
      cin >> x >> y;
      node[i] = P(x, y);
      vis[i] = 0;
    }
    int cnt = 0;
    FOR(i,1,n){
      FOR(j,i+1,n){
        a[cnt].x = i; a[cnt].y = j;
        a[cnt].dis = getdis(node[i], node[j]);
        cnt++;
      }
    }
    sort(a, a + cnt, cmp);
    FOR(i,0,cnt-1){
      if(!vis[a[i].x]&&!vis[a[i].y]){
        vis[a[i].x] = vis[a[i].y] = 1;
      }
    }
    if(vis[1])
      cout << "YES" << endl;
    else
      cout << "NO" << endl;
  }
  return 0;
}

1009

题意

  给你两个数 x , y x,y x,y,让你构造最长上升子序列的长度为 x x x,最长下降子序列长度为 y y y 的序列。

解法

  构造,我们从最长下降子序列入手,首先选择最大的 y y y 个数逆序放在最后,这样就可以满足题目要求了,选最大的是因为留给上升序列给多的上升空间,贪心思想。然后因为你选的是最大的 y y y 个数,所以这个序列对上升序列的长度贡献度是1,这样就把问题转化成了一个用剩下的 n − y n-y ny 个数字构造一个最长上升子序列的长度为 x − 1 x-1 x1,最长下降子序列长度不超过 y y y 的序列的子问题,迭代和递归都可以。

代码
deque<int> v;
int main(){
  IO;
  int T;
  cin >> T;
  while(T--){
    v.clear();
    int n, x, y;
    cin >> n >> x >> y;
    if(x+y>n+1) {
      cout << "NO" << endl;
      continue;
    }
    if((x==1&&y!=n)||(y==1&&x!=n)){
      cout << "NO" << endl;
      continue;
    }
    if((n/y+(n%y>0))>x){
      cout << "NO" << endl;
      continue;
    }
    cout << "YES" << endl;
    while(1){
      FOR(i,n-y+1,n){
        v.push_front(i);
      }
      x--;
      n = n - y;
      y = min(n - x + 1, y);
      if(x==n) break;
    }
    RFOR(i,x,1){
      v.push_front(i);
    }
    int f = 1;
    while(!v.empty()){
      if(f){
        f = 0;
        cout << v.front();
        v.pop_front();
      }
      else{
        cout <<" "<< v.front();
        v.pop_front();
      }
    }
    cout << endl;
  }
  return 0;
}

1010

题意

  起点的x,y的最大公因数>1,每一次都可以朝周围八个方向中gcd(x,y)>1的点走一步或原地停留,每种操作等可能,求经过无限次后能回到原点的概率。

解法

  首先如果起点与对角线连通,那么答案就是 。否则,可以证明起点所在连通块的大小不会很大,暴力 出所有点建图即可。建出图来之后,答案就是 “起点的度数+1” 除以 “总度数+n”,其中 表示连通块大小。

代码
#include <bits/stdc++.h>
#define eps 1e-3
#define pi acos(-1.0)
#define inf 0x3f
#define INF 0x3f3f3f3f
#define pb push_back
#define debug1 cout<<"&&";
#define debug2 cout<<"**";
#define ms(a, x) memset(a, x, sizeof(a))
#define rep(i, a, b) for(int i = a; i <= b; ++i)
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<long long, int> pli;
typedef pair<long long, long long> pll;
const int mod = 1e9 + 7;
const int N = 2e2+10;
const int M = 1e6+10;
inline ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
inline ll Pow(ll a, ll b) { ll ans = 1; for( ; b; b >>= 1) { if(b&1) ans = ans * a % mod; a = a * a % mod;} return ans;}
inline ll Mul ( ll a, ll b)  { ll lf = a * ( b >> 25ll ) % mod * ( 1LL << 25 ) % mod; ll rg = a * ( b & ( ( 1ll << 25 ) - 1 ) ) % mod; return ( lf + rg ) % mod;}
inline ll C(ll y, ll x) { ll ans = 1; if(y < 0 || x < 0 || y < x) return 0; y %= mod; if(!y || !x) return 1; rep(i, 0, x-1) ans = ans * (y-i) % mod; rep(i, 1, x) ans = ans * Pow(i, mod-2) % mod; return ans; }
//==================================================================================================================================================================//

int _, nx[8] = {0, 0, 1, 1, 1, -1, -1, -1}, ny[8] = {-1, 1, -1, 1, 0, -1, 1, 0};
ll x_, y_, sumup, sumdown;
map<pll, int> mp;

bool bfs() {
   queue<pll> q; q.push(pll(x_, y_)); mp[pll(x_, y_)] = 1;
   while(!q.empty()) {
      pll now = q.front(); q.pop();
      // 记录呆在原地
      if(now.first == x_ && now.second == y_) sumup++;
      sumdown++;
      rep(i, 0, 7) {
         ll x = now.first + nx[i], y = now.second + ny[i];
         if(gcd(x, y) == 1) continue;
         if(x == y) {
            sumup = 0, sumdown = 1;
            return false;
         }
         // 记录出去的可能数
         if(now.first == x_ && now.second == y_) sumup++;
         sumdown++;
         if(mp[pll(x, y)]) continue; q.push(pll(x, y));
         // 防止重复寻找
         mp[pll(x, y)] = 1;
      }
   }
   return true;
}
int main() {
   for(scanf("%d", &_); _--; ) {
      sumup = sumdown = 0; mp.clear();
      scanf("%lld %lld", &x_, &y_);
      if(x_ == y_ || !bfs()) printf("0/1\n");
      else {
         ll tmp = gcd(sumdown, sumup);
         printf("%lld/%lld\n", sumup / tmp, sumdown / tmp);
      }
      
   }
   return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值