2022“杭电杯”中国大学生算法设计超级联赛(4)

链接

\(Link with Bracket Sequence II\)

为了方便去重,我们令 \(f_{i,j}\) 表示 \(i\) ~ \(j\) 组成的两端括号匹配的合法括号序列方案数,\(g_{i,j}\) 表示 \(i\) ~ \(j\) 组成的合法括号序列方案数,答案为 \(g_{1,n}\)
转移 \(g_{i,j} -> f_{i-1,j+1}\)\(g_{i,j}=\sum\limits_{k=i}^{j-1}g_{i,k} \cdot f_{k+1,j}\)

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=5e2+3,p=1e9+7;
int n,m,f[N][N],g[N][N],a[N];
IL LL in(){
    char c;int f=1;
    while((c=getchar())<'0'||c>'9')
      if(c=='-') f=-1;
    LL x=c-'0';
    while((c=getchar())>='0'&&c<='9')
      x=x*10+c-'0';
    return x*f;
}
IL int mod(int x){return x>=p?x-p:x;}
IL void solve(){
    memset(f,0,sizeof(f)),memset(g,0,sizeof(g));
    n=in(),m=in();
    for(int i=1;i<=n;++i) a[i]=in();
    for(int i=1;i<=n+1;++i) g[i][i-1]=1;
    for(int l=2;l<=n;++l)
      for(int i=1;i+l-1<=n;++i){
          int j=i+l-1;
          if(!a[i]&&!a[j]) f[i][j]=1ll*g[i+1][j-1]*m%p;
          else if((a[i]>0&&!a[j])||(!a[i]&&a[j]<0)||(a[i]>0&&!(a[i]+a[j]))) f[i][j]=g[i+1][j-1];
          else f[i][j]=0;
          g[i][j]=f[i][j];
          for(int k=i;k<j;++k) g[i][j]=mod(g[i][j]+1ll*g[i][k]*f[k+1][j]%p);
      }
    printf("%d\n",g[1][n]);
}
int main()
{
    int T=in();
    while(T--) solve();
    return 0;
}

\(Link with Running\)

先求出最短路图,因为有 \(0\) 边,所以有环,可以卡 \(spfa\) 。故先 \(tarjan\) 缩点再拓扑排序求值。

#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define pb push_back
using namespace std;
const int N=1e5+3;
struct hh{
    int to,e,p;
};
struct kk{
    int id;LL dis;
    bool operator<(const kk &a) const{
    return dis>a.dis;}
};
vector<hh>e1[N],e2[N],e3[N];
priority_queue<kk>q;
int n,m,vis[N],num,dfn[N],low[N],col[N],Col,sta[N],tp,deg[N];
LL d1[N],f[N];
IL int in(){
    char c;int f=1;
    while((c=getchar())<'0'||c>'9')
      if(c=='-') f=-1;
    int x=c-'0';
    while((c=getchar())>='0'&&c<='9')
      x=x*10+c-'0';
    return x*f;
}
IL void dij(){
    memset(d1+1,63,8*n),memset(vis+1,0,4*n);
    q.push((kk){1,0}),d1[1]=0;
    while(q.size()){
        kk u=q.top();q.pop();
        if(vis[u.id]) continue;
        vis[u.id]=1;
        for(hh v:e1[u.id])
          if(d1[v.to]>d1[u.id]+v.e){
              d1[v.to]=d1[u.id]+v.e;
              q.push((kk){v.to,d1[v.to]});
          }
    }
}
void tarjan(int u){
    dfn[u]=low[u]=++num,sta[++tp]=u;
    for(hh v:e2[u])
      if(!dfn[v.to]) tarjan(v.to),low[u]=min(low[u],low[v.to]);
      else if(!col[v.to]) low[u]=min(low[u],dfn[v.to]);
    if(low[u]==dfn[u]){
        ++Col;
        while(sta[tp+1]^u) col[sta[tp--]]=Col;
    }
}
IL void topo(){
    memset(deg+1,0,4*n),memset(f+1,-63,8*n),f[col[1]]=0;
    for(int i=1;i<=n;++i)
      for(hh u:e2[i])
        if(col[i]^col[u.to])
          e3[col[i]].pb((hh){col[u.to],u.e,u.p}),++deg[col[u.to]];
    for(int i=1;i<=Col;++i) if(!deg[i]) sta[++tp]=i;
    while(tp){
        int u=sta[tp--];
        for(hh v:e3[u]){
            f[v.to]=max(f[v.to],f[u]+v.p);
            if(!--deg[v.to]) sta[++tp]=v.to;
        }
          
    }
}
IL void solve(){
    int x,y,e,p;
    n=in(),m=in();
    for(int i=1;i<=n;++i)
      e1[i].clear(),e2[i].clear(),e3[i].clear();
    for(int i=1;i<=m;++i)
      x=in(),e1[x].pb((hh){in(),in(),in()});
    dij();
    for(int i=1;i<=n;++i)
      for(hh u:e1[i])
        if(d1[i]+u.e==d1[u.to])
          e2[i].pb(u);
    num=tp=Col=0,memset(dfn+1,0,4*n),memset(low+1,0,4*n),memset(col+1,0,4*n);
    tarjan(1),topo();
    printf("%lld %lld\n",d1[n],f[col[n]]);
}
int main()
{
    int T=in();
    while(T--) solve();
    return 0;
}

\(Magic\)

差分约束模板题。

#define IL inline
#define LL long long
using namespace std;
const int N=1e4+3;
struct hh{
    int to,nxt,w;
}e[N*3];
int n,m,k,num,fir[N],vis[N],cnt[N];
LL dis[N];
IL LL in(){
    char c;int f=1;
    while((c=getchar())<'0'||c>'9')
      if(c=='-') f=-1;
    LL x=c-'0';
    while((c=getchar())>='0'&&c<='9')
      x=x*10+c-'0';
    return x*f;
}
IL void add(int x,int y,int w){e[++num]=(hh){y,fir[x],w},fir[x]=num;}
queue<int>q;
void spfa(){
    while(q.size()) q.pop();
    memset(dis,-63,8*(n+1));
    memset(cnt,0,4*(n+1));
    memset(vis,0,4*(n+1));
    dis[0]=0,q.push(0);
    while(!q.empty()){
        int u=q.front();q.pop();vis[u]=0;
        for(int i=fir[u],v;v=e[i].to,i;i=e[i].nxt)
          if(dis[v]<dis[u]+e[i].w){
              dis[v]=dis[u]+e[i].w;
              cnt[v]=cnt[u]+1;
              if(cnt[v]>=n+1){puts("-1");return;}
              if(!vis[v]) q.push(v),vis[v]=1;
          }
    }
    printf("%lld\n",dis[n]);
}
void solve(){
    int l,r,x;
    memset(fir,0,4*(n+1));
    n=in(),k=in(),num=0;
    for(int i=1;i<=n;++i) add(max(1,i-k+1)-1,min(n,i+k-1),in());
    for(int i=1;i<=n;++i) add(i-1,i,0);
    m=in();
    for(int i=1;i<=m;++i){
        l=in(),r=in(),x=in();
        add(r,l-1,-x);
    }
    spfa();
}
int main()
{
    int T=in();
    while(T--) solve();
    return 0;
}

\(Link with Level Editor II\)

注意到 \(m\) 很小,我们可以用矩阵来储存边快速计算情况,用线段树快速求一段的方案情况,双指针跑即可。
蹲一个对顶栈做法。

#pragma GCC optimize(3)
#include <bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N = 5e3 + 3, M = 21;
int n, Max, m, k, flag;
struct Martix {
    int r, c;
    LL a[M][M];
    IL void init(int r, int c) {
        this->r = r, this->c = c;
        for (int i = 1; i <= r; ++i)
            for (int j = 1; j <= c; ++j)
                a[i][j] = 0;
    }
    Martix operator*(const Martix &b) const {
        Martix d;
        d.init(r, b.c);
        for (int i = 1; i <= r; ++i)
            for (int j = 1; j <= b.c; ++j)
                for (int k = 1; k <= c; ++k)
                    d.a[i][j] += a[i][k] * b.a[k][j];
        for (int i = 1; i <= r; ++i)
            for (int j = 1; j <= b.c; ++j)
                if (d.a[i][j] > k)
                    d.a[i][j] = k + 1;
        return d;
    }
    IL int chk() { return a[1][m] <= k; }
} S[N], A, B;
struct segment {
#define ls k << 1
#define rs k << 1 | 1
    Martix s[N << 2];
    void build(int k, int l, int r) {
        if (l == r) {
            s[k] = S[l];
            return;
        }
        int mid = l + r >> 1;
        build(ls, l, mid), build(rs, mid + 1, r);
        s[k] = s[ls] * s[rs];
    }
    Martix query(int k, int l, int r, int ll, int rr) {
        if (l >= ll && r <= rr)
            return s[k];
        int mid = l + r >> 1;
        if (rr <= mid)
            return query(ls, l, mid, ll, rr);
        if (ll > mid)
            return query(rs, mid + 1, r, ll, rr);
        return query(ls, l, mid, ll, rr) * query(rs, mid + 1, r, ll, rr);
    }
} T;
IL int pd(int len) {
    for (int i = len; i <= n; ++i) {
        int j = i - len + 1;
        if (T.query(1, 1, n, j, i).chk())
            return 1;
    }
    return 0;
}
void solve() {
    scanf("%d %d %d", &n, &m, &k);
    for (int i = 1; i <= n; ++i)
        S[i].init(m, m);
    for (int i = 1; i <= n; ++i) {
        int l, x, y;
        scanf("%d", &l);
        while (l--)
            scanf("%d %d", &x, &y), S[i].a[x][y]++;
        for (int j = 1; j <= m; ++j)
            S[i].a[j][j]++;
    }
    T.build(1, 1, n);
    int l = 1, r = 1, ans = 1;
    A = S[1];
    for (; l <= n; ++l, A = T.query(1, 1, n, l, r)) {
        while (r < n && (B = A * S[r + 1], B.chk()))
            ++r, A = B;
        ans = max(ans, r - l + 1);
        if (r == n) break;
    }
    printf("%d\n", ans);
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--)
        solve();
    return 0;
}

\(BIT Subway\)

签到题,写一个分段函数即可(但赛时我智障惹)。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define LF double
using namespace std;
const int N=1e5+3;
const LF p1=0.8,p2=0.5;
int n,a[N];
IL LL in(){
	char c;int f=1;
	while((c=getchar())<'0'||c>'9')
	  if(c=='-') f=-1;
	LL x=c-'0';
	while((c=getchar())>='0'&&c<='9')
	  x=x*10+c-'0';
	return x*f;
}
IL LF ask1(LF x,LF y){
	if(x<100&&x+y>=100) return 100-x+ask1(100,y+x-100);
	if(x<200&&x+0.8*y>=200) return 200-x+(y-(200-x)/0.8)*0.5;
	if(x<100) return y;
	if(x<200) return 0.8*y;
	return 0.5*y;
}
IL LF ask2(LF x,LF y){
	if(x<100) return y;
	if(x<200) return 0.8*y;
	return 0.5*y;
}
void solve(){
	n=in();LF ans1=0,ans2=0;
	for(int i=1;i<=n;++i) a[i]=in();
	for(int i=1;i<=n;++i) ans1+=ask1(ans1,a[i]),ans2+=ask2(ans2,a[i]);
	printf("%.3lf %.3lf\n",ans1,ans2);
}
int main()
{
	int T=in();
	while(T--) solve();
	return 0;
}

\(Climb Stairs\)

假设我们在 \(now\) 层,我们必然是跳到 \(now+x\) 层,再往下走到 \(now+1\) 层,显然每次跳的楼层尽可能少会更优。
我们先求出到 \(now+1\) 层的必要条件,即假设我们跳到某 \(now+x\) 层再向下可以全吃掉能否在 \(now+1\) 层时能够打败怪物,这个可以二分求出。
但我们是默认 \(now+2\)\(now+x\) 层是可以全吃掉的,事实上我们并不知道,所以我们需要再对这些层也进行上述操作,直到满足条件。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
#define LF double
using namespace std;
const int N=1e5+3;
int n,k,a[N];
LL pre[N],st;
IL LL in(){
	char c;int f=1;
	while((c=getchar())<'0'||c>'9')
	  if(c=='-') f=-1;
	LL x=c-'0';
	while((c=getchar())>='0'&&c<='9')
	  x=x*10+c-'0';
	return x*f;
}
IL int get(LL s,int pos){
	int l=pos,r=n;
	while(l<r){
		int mid=l+r>>1;
		if(s+pre[mid]-pre[pos]>=a[pos]) r=mid;
		else l=mid+1;
	}
	return l;
}
void solve(){
	n=in(),st=in(),k=in();
	for(int i=1;i<=n;++i) a[i]=in(),pre[i]=a[i]+pre[i-1];
	int now=0,up=0;
	while(up<n){
		int Max=up+1,i=up;
		while(i<Max){
			int to=get(st,++i);
			if(st+pre[to]-pre[i]<a[i]){puts("NO");return;}
			Max=max(Max,to);
		}
		if(Max>now+k){puts("NO");return;}
		st+=pre[Max]-pre[up],now=up+1,up=Max;
	}
	puts("YES");
}
int main()
{
	int T=in();
	while(T--) solve();
	return 0;
}

\(Fall with Full Star\)

嫖一位学长的博客,里面讲得很详细。
链接

\(Link is as bear\)

如果序列中有连续的两个 \(0\) ,最终序列可以是任意数的异或和。
如果我们要不选这两个 \(0\) 旁边的数,我们可以连续进行两次操作把那个数也变成 \(0\),如果要选择,我们可以通过操作把那个数移到一边,再把要删去的数删去。
于是,如果我们记要选的数标记为 \(1\),不选的标记为 \(0\)
若有两个相连的 \(0\) ,我们可以把它们的数值变为 \(0\)
若有多个相连的 \(1\) ,我们可以把它们合并到一个位置中,并产生相连的 \(0\)
对于 \(0,1\) 相间的情况,因为存在 \(i<j\)\(a_i=a_j\) ,我们可以任意决定 \(i,j\) 的取法使得这种情况不存在。
线性基求出最大异或和即可。

#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=1e5+3;
int n,a[N];
IL LL in(){
    char c;int f=1;
    while((c=getchar())<'0'||c>'9')
      if(c=='-') f=-1;
    LL x=c-'0';
    while((c=getchar())>='0'&&c<='9')
      x=x*10+c-'0';
    return x*f;
}
struct LB{
    LL a[64];
    IL void clear(){memset(a,0,sizeof(a));}
    IL void ins(LL x){
        for(int i=63;~i;--i)
          if(x>>i&1){
              if(a[i]) x^=a[i];
              else{a[i]=x;return;}
          }
    }
    IL LL get_Max(){
        LL Max=0;
        for(int i=63;~i;--i)
          if((Max^a[i])>Max) Max^=a[i];
        return Max;
    }
}B;
void solve(){
    n=in();B.clear();
    while(n--) B.ins(in());
    printf("%lld\n",B.get_Max());
}
int main()
{
    int T=in();
    while(T--) solve();
    return 0;
}
posted @ 2022-08-04 02:07  (o-ωq)).oO  阅读(37)  评论(0编辑  收藏  举报