GDOI2018模拟赛-Round6

protection

题意

给你一个图,只有i和i+1连边,然后还有三对点连边,问m次每对点的最短距离,边的距离都是1 T组数据
T5,n105,m3×105 T ≤ 5 , n ≤ 10 5 , m ≤ 3 × 10 5

分析

枚举这三对点使用的情况以及顺序,其实是一个常数,暴力就过了

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e5+10;
const ll Mod = 1e9+7;
inline ll read()
{
  ll p=0; ll f=1; char ch=getchar();
  while(ch<'0' ||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}
ll t; pair<ll,ll> pr[N]; ll sum[N]; bool vis[N]; ll b[N];

ll ans; ll l,r;

void dfs(ll x)
{
  ll s = 0; ll lst = l;
  for(ll i=1;i<x;i++)
  {
    ll xx = pr[b[i]].first; ll yy = pr[b[i]].second;
    if(abs(xx-lst) > abs(yy-lst)) swap(xx,yy);
    s += abs(xx - lst); s++; lst = yy;
  }s += abs(r - lst); ans = min(ans,s);
  if(x==3+1) return ;
  for(ll i=1;i<=3;i++) if(!vis[i]){vis[i] = 1; b[x] = i; dfs(x+1); vis[i] = 0;}
}

int main()
{
  freopen("protection.in","r",stdin);
  freopen("protection.out","w",stdout);
  t = read();
  while(t--)
  {
    ll n = read(); ll m = read();
    for(ll i=1;i<=3;i++) pr[i].first = read(),pr[i].second = read();
    ll s = 0;
    for(ll p=1;p<=m;p++)
    {
      l = read(); r = read(); ans = INT_MAX;
      if(l>r) swap(l,r);
      for(ll i=1;i<=3;i++) vis[i] = 0 ; dfs(1);
      s = (s + ans * p) % Mod;
    }
    printf("%lld\n",s);
  }
  // cout<<(double)(clock())<<endl;
  return 0;
}

lonely

题意

T组数据,给你N,表示一排N个格子,都是白的,求出至少有一个黑格子,且每个黑格子至少都有一个黑格子与这个黑格子相邻的方案数
T50000,n1018 T ≤ 50000 , n ≤ 10 1 8

分析

简单的矩阵乘法一下,首先有
g[i]=f[i1]+1 g [ i ] = f [ i − 1 ] + 1
f[i]=f[i1]+i=0i2g[i] f [ i ] = f [ i − 1 ] + ∑ i = 0 i − 2 g [ i ]
其中 f f 表示答案,g表示最右的一格必须为白格且合法的方案数
这样矩阵乘法是5*5的,还是不行
考虑只要维护这个格子和上一个格子的状态即 f[i][sta] f [ i ] [ s t a ] 即可,这样就是4*4的
好像还有3*3的?
其实可以oeis一波没毛病

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 110;
const ll Mod = 998244353;
inline ll read()
{
  ll p=0; ll f=1; char ch=getchar();
  while(ch<'0' ||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}

/*ll f[N][N][N]; ll g[N];
*/

struct node
{
  ll a[5][5];
  node(){memset(a,0,sizeof(a));}
  friend node operator * (node x,node y)
  {
    node z;
    for(ll i=0;i<4;i++) for(ll j=0;j<4;j++) for(ll k=0;k<4;k++)
      z.a[i][j] = (z.a[i][j] + x.a[i][k] * y.a[k][j]) % Mod;
    return z;
  }
  friend node operator ^ (node x,ll k)
  {
    node z; for(ll i=0;i<4;i++) z.a[i][i] = 1;
    while(k)
    {
      if(k&1) z=z*x;
      x=x*x; k>>=1;
    }return z;
  }
}s,p;

int main()
{
  freopen("lonely.in","r",stdin);
  freopen("lonely.out","w",stdout);

  p.a[0][1] = p.a[0][0] = 1;
  p.a[1][3] = 1;
  p.a[2][0] = p.a[2][1] = 1;
  p.a[3][3] = p.a[3][2] = 1;

  ll n;

  while(scanf("%lld",&n)!=EOF)
  {
    if(n==0) break;
    s.a[0][0] = s.a[0][1] = 1; s.a[0][2] = s.a[0][3] = 0;
    node now = p^(n-1);
    s = s * now;
    printf("%lld\n",(s.a[0][3] + s.a[0][2] + s.a[0][0] - 1) % Mod);
  }  
  // cout << (double) (clock()) << endl;
  return 0;
}

meiliangxing

题意

给出一个N,找出一对数(i,j),使得i>j 且i的二进制的1的个数 < j的二进制的1的个数
N10300 N ≤ 10 300

分析

看到这么大,先考虑数位dp
好像只确定一个数来数位dp,顶格的情况很难算
其实可以确定两个数来dp
dp[i][j][0/1][0/1][0/1] d p [ i ] [ j ] [ 0 / 1 ] [ 0 / 1 ] [ 0 / 1 ] 表示从最高位起到i位,i和j差多少个1,i和j分别有无顶格,i和j是否相同,然后就做完了

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1510;
const ll Mod = 998244353;
inline ll read()
{
  ll p=0; ll f=1; char ch=getchar();
  while(ch<'0' ||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}

char ss[N];

ll len;
ll Divide(ll *a)
{
  ll lst = 0;
  for(ll i=len;i>=1;i--)
  {
    if(lst) a[i] += 10;
    if(a[i] & 1) lst = 1; else lst = 0;
    a[i]>>=1;
  }
  while(a[len] == 0 && len) len--;
  return lst;
}

ll a[N];
ll b[N]; ll blen = 0;

void upd(ll &x,ll y){x=(x+y)%Mod;}

ll f[2][3010][2][2][2] , now = 1;

int main()
{
  freopen("meiliangxing.in","r",stdin);
  freopen("meiliangxing.out","w",stdout);

  ll t = read();
  while(t--)
  {
    scanf("%s",ss+1); len = strlen(ss+1);
    for(ll i=len;i>=1;i--) a[i] = ss[len - i + 1] - '0';
    blen = 0; while(len) b[++blen] = Divide(a);

    memset(f,0,sizeof(f));
    now = 0; f[now][1500][1][1][1] = 1;
    for(ll i=blen;i>=1;i--,now = now ^ 1)
    {
      memset(f[now^1],0,sizeof(f[now^1]));
      for(ll p=0;p<=3000;p++)
        for(ll op1 = 0;op1 < 2;op1++)
          for(ll op2 = 0;op2 < 2;op2++)
            for(ll op = 0;op < 2;op++) if(f[now][p][op1][op2][op])
              for(ll j = 0;j < 2;j++) for(ll k = 0;k < 2;k++)
              {
                if(op == 1 && j > k) continue;
                if(op1 == 1 && j > b[i]) continue;
                if(op2 == 1 && k > b[i]) continue;
                ll n = op & (j == k);
                ll n1 = op1 & (j==b[i]);
                ll n2 = op2 & (k==b[i]);
                upd(f[now^1][p + j - k][n1][n2][n] , f[now][p][op1][op2][op]);
                //printf("%lld %lld %lld %lld <- %lld %lld %lld %lld\n",p+j-k,n1,n2,n,p,op1,op2,op);
              }
    }
    ll ans = 0;
    for(ll p=1501;p<=3000;p++) for(ll op1=0;op1<2;op1++) for(ll op2=0;op2<2;op2++) if(f[now][p][op1][op2][0])
    {
      upd(ans , f[now][p][op1][op2][0]);
      //printf("%lld %lld %lld %lld\n",p,op1,op2,f[now][p][op1][op2][0]);
    }
    printf("%lld\n",ans);
  }

  return 0;
}

grancrevasse

题意

给你一个长度为N的0,1串,m个操作,要求满足两种操作:
异或某一位
如果有01这样的组合就取出,可以连续,且询问独立,询问区间的消去后的第k个位置是哪个位置

1n,m5×105 1 ≤ n , m ≤ 5 × 10 5

分析

解法一般是线段树
取出后肯定是111111111111110000000000000的形式
那么只要合并的时候把左孩子的0和右孩子的1消掉就好
考虑查找,二分查找 O(mlog2n) O ( m l o g 2 n ) ,看看有没有更优的复杂度
直接在线段树上查找,分开看看是查找0,还是查找1
(考虑区间合并要取出,不能一起查找)
查找0的话从右端点找,否则从左端点开始找,时间复杂度 O(mlogn) O ( m l o g n )

代码
#include <bits/stdc++.h>
using namespace std;
const int N = 500010;
inline int read()
{
  int p=0; int f=1; char ch=getchar();
  while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
  while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
  return p*f;
}

char ss[N];

int rt,tot,lc[N<<2],rc[N<<2],c0[N<<2],c1[N<<2];

void upd(int u)
{
  if(c0[lc[u]] >= c1[rc[u]])
  {
    c1[u] = c1[lc[u]];
    c0[u] = c0[lc[u]] + c0[rc[u]] - c1[rc[u]];
  }
  else
  {
    c1[u] = c1[rc[u]] + c1[lc[u]] - c0[lc[u]];
    c0[u] = c0[rc[u]];
  }
}

int build(int L,int R)
{
  int u = ++tot;
  if(L<R)
  {
    int mid=(L+R)>>1;
    lc[u] = build(L,mid);
    rc[u] = build(mid+1,R);
    upd(u);
  }
  else
  {
    if(ss[L] == '0') c0[u] = 1,c1[u] = 0;
    else c1[u] = 1,c0[u] = 0;
  }return u;
}

void chg(int u,int L,int R,int k)
{
  if(L==R){if(ss[L] == '0') c0[u] = 1,c1[u] = 0; else c1[u] = 1,c0[u] = 0; return ;} 
  int mid = (L+R)>>1;
  if(k<=mid) chg(lc[u],L,mid,k);
  else chg(rc[u],mid+1,R,k);
  upd(u);
}

int s0 , s1;

#define MP make_pair

pair<int,int> merge(pair<int,int> x,pair<int,int> y)
{
  if(x.second > y.first) return MP(x.first , x.second - y.first + y.second);
  else return MP(x.first + y.first - x.second , y.second);
}
pair<int,int> qry(int u,int L,int R,int l,int r)
{
  if(L == l && R == r){return MP(c1[u],c0[u]);}
  int mid=(L+R)>>1;
  if(r<=mid) return qry(lc[u],L,mid,l,r);
  else if(l>mid) return qry(rc[u],mid+1,R,l,r);
  else return merge(qry(lc[u],L,mid,l,mid),qry(rc[u],mid+1,R,mid+1,r));
}

int find_l(int u,int L,int R,int pos,int k)
{
  if(L==R && k==c1[u]) return L;
  if(pos==L && c1[u] < k) return R + k - c1[u] + c0[u];
  int mid = (L+R)>>1;
  if(pos > mid) return find_l(rc[u],mid+1,R,pos,k);
  int tmp = find_l(lc[u] , L , mid , pos, k);
  if(tmp <= mid) return tmp;
  else return find_l(rc[u], mid+1, R, mid+1, tmp - mid );
}

int find_r(int u,int L,int R,int pos,int k)
{
  if(L==R && k==c0[u]) return R;
  if(pos == R && c0[u] < k) return L - k + c0[u] - c1[u];
  int mid = (L+R)>>1;
  if(pos <= mid) return find_r(lc[u],L,mid,pos,k);
  int tmp = find_r(rc[u],mid+1,R,pos,k);
  if(tmp > mid) return tmp;
  else return find_r(lc[u],L,mid,mid, mid+1 - tmp);
}


int main()
{
  freopen("grancrevasse.in","r",stdin);
  freopen("grancrevasse.out","w",stdout);

  int n = read() , m = read();

  for(int i=1;i<=n;i++) scanf("\n%c",&ss[i]);

  tot = 0; rt = build(1,n);

  while(m--)
  {
    int op = read();
    if(op==1)
    {
      int x = read(); ss[x] = (ss[x] == '0') ? '1' : '0'; chg(rt,1,n,x);
    }
    else
    {
      int l = read(); int r = read(); int k = read();
      s0 = 0; s1 = 0;
      pair<int,int> x = qry(rt,1,n,l,r);
      s0 = x.first; s1 = x.second;
      if(s0 + s1 < k) printf("-1\n");
      else
      {
        if(k <= s0) printf("%d\n",find_l(rt,1,n,l,k));
        else printf("%d\n",find_r(rt,1,n,r,s0+s1-k+1));
      }
    }
  }

  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值