CF1472(div3):总结

前言

周围的大佬都在切蓝紫黑
只有我在水div3的水题
qwq

A. Cards for Friends

题意简述

给出一个矩形的长和宽,当它的长(或宽)是偶数时,你就可以把它按照长(或宽)的方向平分成相等的两半
求该矩形能否剪出大于等于n个矩形
w , h ≤ 1 0 4 , n ≤ 1 0 9 w,h\leq 10^4,n\leq10^9 w,h104,n109

解析

显然长和宽是独立的
分别求出两个方向最多能剪多少再乘在一起就行了

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=105;
const int mod=998244353;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}

int n,m;

char s[N];

int main(){
  #ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
  #endif
  int T=read();
  while(T--){
    scanf(" %s",s+1);
    n=strlen(s+1);
    if(s[1]!=s[n]) s[1]=195-s[1];
    printf(" %s\n",s+1);
  }
  return 0;
}

B. Fair Division

题意简述

给出一些价值为1或2的元素,求是否存在一种方案,把元素分成两个集合后两集合价值和相等
n ≤ 100 n\leq 100 n100

解析

设所有元素价值和为sum
当且仅当sum为奇数,或者sum/2为奇数且没有1的元素时,无解

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=105;
const int mod=998244353;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}

int n,m;

char s[N];
int a,b,sum;
int main(){
  #ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
  #endif
  int T=read();
  while(T--){
    n=read();a=0;b=0;sum=0;
    for(int i=1;i<=n;i++) sum+=read();
    a=sum-n;b=n-a;
    if((sum&1)||(b==0&&sum%4)) printf("NO\n");
    else printf("YES\n");
  }
  return 0;
}

C. Long Jumps

题意简述

给出一个数列 a 1 − a n a_1-a_n a1an,你可以从任何一个位置 i 开始,每次获得 a i a_i ai的分数, 并跳到 i + a i i+a_i i+ai 的位置,直到跳出去为止
n ≤ 2 × 1 0 5 , 1 ≤ a i ≤ 1 0 9 n\leq 2\times 10^5,1 \leq a_i \leq 10^9 n2×105,1ai109

解析

直接按题意模拟dp即可
注意数组越界

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=2e5+100;
const int mod=998244353;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}

int n,m;

int a[N],dp[N];
int main(){
  #ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
  #endif
  int T=read();
  while(T--){
    n=read();int mx(0);
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=n;i>=1;i--){
      if(i+a[i]>n) dp[i]=a[i];
      else dp[i]=dp[i+a[i]]+a[i];
      mx=max(mx,dp[i]);
    }
    printf("%d\n",mx);
  }
  return 0;
}

D. Even-Odd Game

题意简述

alice和bob玩游戏
他们有一个长度为n的数列 a a a,从alice开始,轮流取数
alice得到所有取到的偶数的分数
bob得到所有取到的奇数的分数
求最后谁的分数高
n ≤ 2 ∗ 1 0 5 , 1 ≤ a i ≤ 1 0 9 n \leq 2*10^5,1 \leq a_i \leq 10^9 n2105,1ai109

解析

显然两个人会不管奇偶的从大往小取
sort一下后模拟即可

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=2e5+100;
const int mod=998244353;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}

int n,m;

ll sum[2];
int a[N];
int main(){
  #ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
  #endif
  int T=read();
  while(T--){
    n=read();sum[1]=sum[0]=0;
    for(int i=n;i>=1;i--){
      a[i]=read();sum[a[i]&1]+=a[i];
    }
    sort(a+1,a+1+n);
    for(int i=n;i>=1;i--){
      if(((n-i+1)&1)==(a[i]&1)) sum[a[i]&1]-=a[i];
    }
    if(sum[1]>sum[0]) printf("Bob\n");
    else if(sum[1]<sum[0]) printf("Alice\n");
    else printf("Tie\n");
  }
  return 0;
}

E. Correct Placement

题意简述

给出n个元素,每个元素有两个关键值域 a a a b b b
我们说元素 u 支配元素 v,当且近当满足下列两个条件之一:
1. a u > a v 且 b u > b v a_u>a_v且b_u>b_v au>avbu>bv
2. b u > a v 且 a u > b v b_u>a_v且a_u>b_v bu>avau>bv
要求对于所有的 1 ≤ i ≤ n 1 \leq i \leq n 1in,输出任意一个元素 i 支配的元素 j
如果不存在输出 -1
n ≤ 2 × 1 0 5 n \leq 2\times 10^5 n2×105

解析

我的做法是树状数组,但是其实并不用…
支配的含义可以简化为:u的最大值大于v的最大值,且u的最小值也大于v的最小值
那么我们按照最小值sort一下,再从前往后扫一遍,沿途记录最大值的最小值w,看是否比当前的最大值小,就行了

代码

然而还是树状数组的码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=4e5+100;
const int mod=998244353;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}

int n,m;

int q[N],a[N],b[N],num;
struct tree{
  int mn[N],id[N];
  void init(int x){fill(mn,mn+1+x,1e9);}
  inline void upd(int p,int v,int nam){
    for(int i=p;i<=num;i+=i&-i){
      if(mn[i]>v){
	mn[i]=v;id[i]=nam;
      }
    }
    return;
  }
  int ask(int p,int v){
    for(;p;p-=p&-p){
      if(mn[p]<v) return id[p];
    }
    return -1;
  }
}t1;
int main(){
  #ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
  #endif
  int T=read();
  while(T--){
    n=read();
    for(int i=1;i<=n;i++){
      a[i]=read();b[i]=read();
      q[++num]=a[i];q[++num]=b[i];
    }
    sort(q+1,q+1+num);
    num=unique(q+1,q+1+num)-q-1;
    t1.init(num);
    for(int i=1;i<=n;i++){
      a[i]=lower_bound(q+1,q+1+num,a[i])-q;
      b[i]=lower_bound(q+1,q+1+num,b[i])-q;
      //printf("i=%d a=%d b=%d\n",i,a[i],b[i]);
      t1.upd(a[i],b[i],i);
    }
    for(int i=1;i<=n;i++){
      int res(0);
      if((res=t1.ask(a[i]-1,b[i]))!=-1) printf("%d ",res);
      else if((res=t1.ask(b[i]-1,a[i]))!=-1) printf("%d ",res);
      else printf("-1 ");
    }
    putchar('\n');
  }
  return 0;
}

F. New Year’s Puzzle

题意简述

给出一个 2 × n 2 \times n 2×n的矩形,其中有m个格子被挡住了,求是否可以用1*2的长方形覆盖满所有未被挡住的格子
n ≤ 1 0 9 , m ≤ 2 × 1 0 5 n \leq 10^9,m \leq 2\times 10^5 n109m2×105

解析

题解似乎是一些分类讨论,但我觉得并不简单,就没仔细看…
n ≤ 2 × 1 0 5 n \leq 2\times 10^5 n2×105时,显然直接dp即可
但是目前n太大了

注意到挡住的格子非常稀疏
不难发现当前一列的填的状态一定,且后面全没被挡住时,填法以两列为一个单位是循环一定的
我们可以强行把相邻的格子的距离%2缩小到O(1)级别
然后大力dp即可

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=4e5+100;
const int mod=998244353;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}

int n,m;

struct node{
  int h,pl;
  bool operator < (const node o){return pl<o.pl;}
}p[N];
bool jd[N][3];
int main(){
  #ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
  #endif
  int T=read();
  while(T--){
    n=read();m=read();
    //int Jd=++tim==377;
    //if(Jd) printf("%d %d\n",n,m);
    for(int i=1;i<=m;i++){
      p[i].h=read();p[i].pl=read();
      //if(Jd) printf("%d %d\n",p[i].h,p[i].pl);
    }
    p[++m]=(node){1,0};p[++m]=(node){2,0};
    p[++m]=(node){1,n+1};p[++m]=(node){2,n+1};
    sort(p+1,p+1+m);
    for(int i=3;i<=m;i++){
      int flag=p[i].pl==p[i+1].pl;
      int d=p[i].pl-p[i-1].pl;
      p[i].pl=p[i-1].pl+1+((d+1)&1);
      if(flag) p[i+1].pl=p[i].pl,++i;
    }
    n=p[m].pl;
    for(int i=1;i<=n;i++) jd[i][1]=jd[i][2]=0;
    for(int i=1;i<=m;i++){
      //printf("i=%d pl=%d h=%d\n",i,p[i].pl,p[i].h);
      jd[p[i].pl][p[i].h]=1;
    }
    int flag(1);
    for(int i=1;i<=n;i++){
      if(jd[i-1][1]&&jd[i-1][2]){
	//printf("  a\n");
	if(!jd[i][1]&&!jd[i][2]) jd[i][1]=jd[i][2]=1;
      }
      else if(!jd[i-1][1]){
	//printf("  b\n");
	if(jd[i][1]){
	  flag=0;break;
	}
	else jd[i][1]=1;
      }
      else{
	//printf("  c\n");
	if(jd[i][2]){
	  flag=0;break;
	}
	else jd[i][2]=1;
      }
      //printf("i=%d jd=%d %d\n",i,jd[i][1],jd[i][2]);
    }
    if(!jd[n][1]||!jd[n][2]) flag=0;
    if(flag) printf("YES\n");
    else printf("NO\n");
  }
  return 0;
}

G. Moving to the Capital

题意简述

有一个由 n 个结点组成的有向图。一些结点由边长为1的有向边相连。
已知一个数组 d 1... n d_{1...n} d1...n​,其中 d i d_i di​ 为从编号为 1 的结点到编号为 i 的结点的最短距离。 你站在第 s 个节点上。你有以下几种选择:

  1. 沿着某条边,从第 i 个结点到第 j 个结点, d i < d j d_i<d_j di<dj
  2. 沿着某条边,从第 iii 个结点到第 jjj 个结点, d i ≥ d j d_i\geq d_j didj​;(最多只能走一次)

你的目标是:靠近编号为 1 的点。(准确地说,是找到从任意一个结点出发到第 1 个结点的最短距离)

解析

把dp设计写在脸上的题
直接设计 d p x , 0 / 1 dp_{x,0/1} dpx,0/1表示x点出发使用/不使用逆行机会的最小距离
递归转移即可
注意:只有在 d i s t o > d i s x dis_{to}>dis_x disto>disx的时候才递归求dp!否则会循环递归出大问题!!

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=4e5+100;
const int mod=998244353;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}

int n,m;

struct node{
  int to,nxt;
}p[N<<1];
int fi[N],cnt;
inline void addline(int x,int y){
  p[++cnt]=(node){y,fi[x]};fi[x]=cnt;
  return;
}

bool vis[N];
int dis[N];

int q[N],st,ed;
void bfs(){
  q[st=ed=1]=1;dis[1]=0;vis[1]=1;
  while(st<=ed){
    int now=q[st++];
    for(int i=fi[now];~i;i=p[i].nxt){
      int to=p[i].to;
      if(vis[to]) continue;
      q[++ed]=to;vis[to]=1;
      dis[to]=dis[now]+1;
    }
  }
  return;
}

int dp[N][2];
void find(int x){
  if(dp[x][0]!=-1) return;
  dp[x][0]=dp[x][1]=dis[x];
  //printf("find:%d\n",x);
  for(int i=fi[x];~i;i=p[i].nxt){
    int to=p[i].to;
    //printf("  x=%d to=%d\n",x,to);
    if(dis[to]>dis[x]){
      find(to);
      dp[x][0]=min(dp[x][0],dp[to][0]);
      dp[x][1]=min(dp[x][1],dp[to][1]);
    }
    else{
      dp[x][1]=min(dp[x][1],dis[to]);
    }
    //printf("  x=%d to=%d dp0=%d dp1=%d\n",x,to,dp[x][0],dp[x][1]);
  }
  return;
}

int main(){
  #ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
  #endif
  int T=read();
  while(T--){
    n=read();m=read();
    fill(vis,vis+1+n,0);
    fill(fi,fi+1+n,-1);cnt=-1;
    for(int i=1;i<=n;i++) dp[i][0]=dp[i][1]=-1;
    for(int i=1;i<=m;i++){
      int x=read(),y=read();addline(x,y);
    }
    bfs();
    for(int i=1;i<=n;i++){
      find(i);
      //printf("i=%d dis=%d dp0=%d dp1=%d\n",i,dis[i],dp[i][0],dp[i][1]);
      printf("%d ",min(dp[i][0],dp[i][1]));
    }
    putchar('\n');
  }
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值