Loading

11.9 正睿做题笔记

T1

这个题的一个想法是我们首先把所有点放入堆里,然后每次从堆中取出权值和最小的团,然后往这个团里每次插入一个可以插的点。但是这样放入堆里的元素个数无法估计,会被卡空间,下面有几种优化:

  1. 首先我们把堆换成 multiset,然后如果取出的元素个数加上集合内元素个数之和大于 \(k\),我们就可以把那些最大的删掉。另一个优化是如果当前元素大于 \(k\) 并且当前插入值比最大值大,我们就不插入。
  2. 我们首先把所有点的权值排序,然后我们每次取出一个团,有两种操作,第一种是去掉最大的那个点,换一个更大的点。或者我们直接往这个团里加一个能加的点中最小的点。
  3. 我们换一种思路,考虑二分这个值,然后考虑是否能找到 \(k\) 个团,使得这些团的权值都小于二分的值,我们考虑从空集开始扩展,每次加上一个能加的点,需要保证加上后权值要比二分值小,否则停止扩展,这样我们最多会扩展 \(k\) 次,然后我们可以用 __int128 来状压哪些点是可以加进当前团的。

代码用的是第一种实现方法。

代码:

#include<iostream>
#include<bits/stdc++.h>
#include<cstdio>
#include<algorithm>
#include<bitset>
#include<queue>
#define ll long long
#define N 110
using namespace std;

bitset<110> v[N];

template<typename T> inline void read(T &x){
	x=0;int flag=1;
	char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') flag*=-1;
	for(;isdigit(c);c=getchar()) x=x*10+c-'0';
	x*=flag;
}

struct Node{
	ll val;
	bitset<110> bit;
	inline Node(){}
	inline bool operator < (const Node &b)const{
		return val<b.val;
	}
};

multiset<Node> S;

int n,k,a[N],cnt;
char s[N];

inline void Init(){
	read(n);read(k);k--;
	for(int i=1;i<=n;i++) read(a[i]);
	for(int i=1;i<=n;i++){
		scanf("%s",s+1);
		for(int j=1;j<=n;j++){
			v[i][j]=s[j]-'0';
		}
	}
	for(int i=1;i<=n;i++){
		Node now;now.bit.reset();
		now.bit[i]=1;now.val=a[i];
        S.insert(now);
	}
}

int main(){
	// freopen("my.in","r",stdin);
	// freopen("my.out","w",stdout);
	Init();
	while(S.size()){
		Node Top=(*S.begin());S.erase(S.begin());
        cnt++;
		if(cnt==k){
			printf("%lld",Top.val);return 0;
		}
		int posi=-1;
		for(int i=1;i<=n;i++) if(Top.bit[i]) posi=i+1;
        while(S.size()+cnt>k) S.erase(--S.end());
		for(int j=posi;j<=n;j++){
			if((v[j]&Top.bit)==Top.bit&&(!(S.size()+cnt>k&&Top.val+a[j]>=(*(--S.end())).val))){
				Top.bit[j]=1;Top.val+=a[j];
				S.insert(Top);
				Top.bit[j]=0;Top.val-=a[j];
			}
		}
	}
	puts("-1");
	return 0;
}

T2

我们首先考虑 \(a_i\) 一定是 \(b_{i-2},b_{i-1},b_i\) 中的一个值,原因是我们只是需要一个相对大小,我们总可以调整调整这个值使其变成 \(b\) 中的值,当然是有解的情况下。所以我们每个位置都有三个取值。设 \(f_{i,j,k}\) 表示 \(i\) 这个数放第 \(i\) 个取值,\(j\) 这个数放第 \(k\) 个取值,是否有解,转移即可。代码实现略有技巧。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 100010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

inline int GetMid(int a,int b,int c){
    if(a>b) swap(a,b);if(b>c) swap(b,c);if(a>b) swap(a,b);
    return b;
}

int b[N],t,n,a[N][3],Pre[N][3][3],ans[N];
bool f[N][3][3];

inline void Clear(){
    for(int i=1;i<=n;i++){
        for(int j=0;j<=2;j++) for(int k=0;k<=2;k++){
            f[i][j][k]=0;Pre[i][j][k]=-1;
        }
    }
}

inline void Init(){
    read(n);
    for(int i=3;i<=n;i++) read(b[i]);
    b[1]=b[2]=b[3];
    b[n+1]=b[n+2]=b[n];
    for(int i=1;i<=n;i++){
        a[i][0]=b[i];a[i][1]=b[i+1];a[i][2]=b[i+2];
        // printf("a[%d][0]=%d a[%d][1]=%d a[2][%d]=%d\n",i,a[i][0],i,a[i][1],i,a[i][2]);
    }
    for(int i=1;i<=2;i++) for(int j=0;j<=2;j++) for(int k=0;k<=2;k++) f[i][j][k]=1;
}

inline void Solve(){
    for(int i=3;i<=n;i++)
        for(int j=0;j<=2;j++)
            for(int k=0;k<=2;k++){
                if(!f[i-1][k][j]) continue;
                for(int l=0;l<=2;l++){
                    if(GetMid(a[i][l],a[i-1][k],a[i-2][j])!=b[i]) continue;
                    f[i][l][k]=1;Pre[i][l][k]=j;
                    // printf("f[%d][%d][%d]=%d\n",i-1,k,j,f[i-1][k][j]);
                    // printf("f[%d][%d][%d]=%d\n",i,l,k,f[i][l][k]);
                    // printf("Pre[%d][%d][%d]=%d\n",i,l,k,Pre[i][l][k]);
                }
            }
    int nowj=-1,nowk=-1;
    for(int j=0;j<=2;j++) for(int k=0;k<=2;k++) if(f[n][j][k]){nowj=j;nowk=k;break;}
    // printf("nowj=%d nowk=%d\n",nowj,nowk);
    if(nowj==-1&&nowk==-1) return(void)puts("-1");
    int now=n;
    while(nowj!=-1&&nowk!=-1){
        ans[now]=a[now][nowj];
        // printf("a[%d][%d]=%d\n",now,nowj,a[now][nowj]);
        int Next=Pre[now][nowj][nowk];nowj=nowk;nowk=Next;now--;
        // printf("Next=%d\n",Next);
        if(Next==-1){
            ans[now]=a[now][nowk];break;
        }
    }
    ans[1]=b[1];
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);puts("");
}

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    memset(f,0,sizeof(f));memset(Pre,-1,sizeof(Pre));
    read(t);
    while(t--){
        Init();Solve();Clear();
    }
    return 0;
}

T3,T4 因为考得不是 NOIP 内容,所以暂且先不补了。

posted @ 2021-11-09 18:29  hyl天梦  阅读(35)  评论(0编辑  收藏  举报