CCPC2022威海补题

B

C

D

根据题意状压DP就好了。我这里的实现应该是比较简洁的,先按照输入的格式,再把下半部分往右移,这样每个点相邻点都可以用同样的方向数组计算。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=20,M=(1<<20);
const int dx[6]={0,1,1,0,-1,-1},dy[6]={1,1,0,-1,-1,0};
int st[N]={0,1,1,1,2,3},ed[N]={0,3,4,5,5,5},id[N][N],to[N][6];
struct node{
	int x,y;
}pos[N];
int n,a[N],f[M],pw[N];
int cal(int x){
	int s=0;
	for(int i=1;i<=n;i++) if(x&pw[i]) s++;
	return s;
}
void pre(){
	for(int i=1;i<=5;i++)
		for(int j=st[i];j<=ed[i];j++) id[i][j]=++n,pos[n]=(node){i,j};
	for(int i=1;i<=5;i++){
		for(int j=st[i];j<=ed[i];j++){
			for(int k=0;k<6;k++){
				int x=i+dx[k],y=j+dy[k];
				if(x<0 || x>5 || y<st[x] || y>ed[x]) continue;
				to[id[i][j]][k]=id[x][y];
			}
		}
	}
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),pw[i]=(1<<(i-1));
	for(int cnt=0;cnt<=n;cnt++){
		for(int i=0;i<(1<<n);i++) if(cal(i)==cnt){
			
			for(int j=1;j<=n;j++) if(i&pw[j]){
				f[i]=max(f[i],f[i-pw[j]]);
				for(int k=0;k<6;k++){
					int u=to[j][k],v=to[j][(k+3)%6];
					if(!u || !v) continue;
					if( i&pw[u] && !(i&pw[v]) ) f[i]=max(f[i],f[i-pw[u]+pw[v]-pw[j]]+a[j]);
				}
			}
		}
	}
}
int main()
{
	pre();
	int q; cin>>q;
	while(q--){
		int s=0;
		for(int i=1;i<=n;i++){
			char ch;
			scanf(" %c",&ch);
			if(ch=='#') s+=pw[i];
		}
		cout<<f[s]<<endl;
	}
	return 0;
}

F

就是根据题意,考虑走的路径,是一段段颜色相同的点,然后算一下答案就是每段和+下一个点取max,然后跑两遍floyed就行了。
这不比G简单多了。。。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=305,M=1e5+5;
const ll F=1e12;
int n,m,c[N],w[N];
struct edge{
	int u,v;
}e[M];
ll d1[N][N],d2[N][N],ans[N][N];
void work(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) scanf("%d",&c[i]);
	for(int i=1;i<=n;i++) scanf("%d",&w[i]);
	//d1
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) d1[i][j]=F;
	for(int i=1;i<=n;i++) d1[i][i]=0;
	for(int u,v,i=1;i<=m;i++){
		scanf("%d%d",&u,&v);
		e[i]=(edge){u,v};
		if(c[u]==c[v]) d1[u][v]=w[u],d1[v][u]=w[v];
	}
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(i!=j && c[i]==c[k] && c[j]==c[k])
			d1[i][j]=min(d1[i][j],d1[i][k]+d1[k][j]);
	}
	//d2
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) d2[i][j]=F;
	for(int i=1;i<=n;i++) d2[i][i]=0;
	for(int i=1;i<=m;i++){
		int u=e[i].u,v=e[i].v;
		if(c[u]==c[v]) continue;
		d2[u][v]=d2[v][u]=w[u]+w[v];
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++) if(j!=i && c[j]==c[i]){
			//d2[i][j]=min(d2[i][j],d1[i][j]+w[j]);
			for(int k=1;k<=n;k++) if(k!=i && k!=j && c[k]!=c[i])
				d2[i][k]=min(d2[i][k],d1[i][j]+d2[j][k]);
		}
	}
	/*for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++) printf("%d ",d2[i][j]);
		puts("");
	}*/
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(i!=j)
			d2[i][j]=min(d2[i][j],max(d2[i][k],d2[k][j]));
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			ans[i][j]=d2[i][j];
			for(int k=1;k<=n;k++) if(c[k]==c[j]) ans[i][j]=min(ans[i][j],max(d2[i][k],d1[k][j]+w[j]));
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++) printf("%lld ",ans[i][j]);
		puts("");
	}
}
int main()
{
	int T; cin>>T; while(T--) work();
	return 0;
}

G

I

J

K

看完题之后思路是很自然的:对于每个要求,就转化成对于l和r的限制。原本被题目解释干扰了,纠结了一下区间长度的限制觉得很复杂;后来发现只要l和r合法,区间长度就合法,所以对于1的限制直接取交,对于2的二选一的限制,直接按照l的限制排序,枚举l所在的区间,即可得到r的范围,再和一开始的交集取交即可。

这思路确实很容易也很好写,但写完疯狂wa2。。。还是有个小细节没注意到,,,有点破防

image
此处要和1取max,因为原本算出来的l可能小于0,原理其实和后面的和L取min是一样的,但1的限制太容易忽略(区间范围关系的计数题,应该把每个限制都写成区间的形式!!而不是只有一边的不等式),,,而且在上面其实考虑过限制1算出来<0的情况,也忘记考虑限制2了,只能说写题的时候脑子比较混乱
image

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
const int N=1e5+5,F=2e9+5;
int T,n,m,L,R;
struct node{
	int l,r;
}p[N];
int mn[N];
bool cmp(node u,node v){
	return u.l<v.l;
}
void work(int id){
	cin>>n;
	m=0;
	L=F; R=1;
	while(n--){
		int t;
		ll k,x;
		scanf("%lld%lld%lld",&t,&k,&x);
		ll s=k*(k-1)/2;
		int l=(int)((x-s)/k),r=(int)((x+s-1)/k+1);
		if(t==1) L=min(L,l),R=max(R,r);
		else p[++m]=(node){l,r};
	}
	if(L<=0){
		puts("0");
		return;
	}
	if(L==F){
		puts("-1");
		return;
	}
	sort(p+1,p+m+1,cmp);
	p[0]=(node){0,0};
	p[m+1]=(node){F,F};
	mn[m+1]=F;
	for(int i=m;i;i--) mn[i]=min(p[i].r,mn[i+1]);
	ll ans=0;
	for(int i=0;i<=m;i++){
		int a=max(1ll,p[i].l+1),b=min(L,p[i+1].l);
		if(a>b || mn[i+1]<=R) continue;
		if(b==F || mn[i+1]==F){
			puts("-1");
			return;
		}
		else ans+=1ll*(b-a+1)*(mn[i+1]-R);
	}
	cout<<ans<<endl;
}
signed main()
{
	cin>>T;
	for(int i=1;i<=T;i++) work(i);
	return 0;
}

posted @   sz[sz]  阅读(166)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示