今天干了三道--7.19

1.Silly Sort

  • 题意:每次操作可以交换两点,但代价为两点的数字和,使得数列有序。
  • 思路:在网络上搜到了一个置换组的名词,之前听nodgd也说过。置换组具有独立的性质,每个置换组单独处理,具体:
    1.用其中最小的点去一次交换每个点
    2.用整个数列最小的点换置换组中最小的点,然后按1,最后换回.
    可以证明没有比1,2两种情况更优的!
  • 代码:
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5;
int a[N],val[N],p[N],n,inf=0x3f3f3f3f;
int main() {
	int t=0;
	while(1) {
		t++;
		int mn=inf,ans=0;
		scanf("%d",&n);
		if(!n) break; 
		for(int i=1;i<=n;i++) scanf("%d",&a[i]),val[i]=a[i],mn=min(a[i],mn);
		sort(val+1,val+1+n);
		for(int i=1;i<=n;i++) p[a[i]]=i;
		for(int i=1;i<=n;i++) {
			int mn2=inf,x=i,sum=0,cnt=0;
			while(a[x]!=-1&&a[x]!=val[x]) {
				mn2=min(mn2,a[x]);
				sum+=a[x];
				a[x]=-1;
				x=p[val[x]];
				cnt++;
			}
			if(mn2==inf) continue;
			sum-=mn2;	//sum里面包括了mn2
			ans+=min((cnt-1)*mn2+sum,(cnt+1)*mn+sum+mn2*2);
		}
		printf("Case %d: %d\n",t,ans);
		puts("");
	}
	return 0;
}

2.Block Compaction

  • 题意:有很多个长方块【方块之间顶多刚好面重合,不可相交】,循环操作(直到两种操作都无法进行):1.将所有长方块尽量往下靠,2.将所有长方块尽量往左靠。
  • 思路:对于1操作,我们按上位置排序,然后用前面的与其有概率下落后重叠的块的上表面的最大值更新下表面下降的位置,如果下面没有直接沉底
    对于2操作,我们按右位置排序,然后同理。
  • 代码:
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5;
struct node {
	int w,h,u,d,l,r;
}a[N];
bool cmp1(node a,node b) {
	return a.u<b.u;
}
bool cmp2(node a,node b) {
	return a.r<b.r;
}
int main() {
	int n,T;
	scanf("%d",&T);
	while(T--) {
		scanf("%d",&n);
		for(int i=1;i<=n;i++) {
			int x,y,p,q;
			scanf("%d%d%d%d",&x,&y,&p,&q);
			a[i]=(node){p-x,q-y,q,y,x,p};
		}
		bool flag;
		do {
			//1
			flag=false;
			sort(a+1,a+1+n,cmp1);
			for(int i=1;i<=n;i++) {
				int mx=-1;
				for(int j=1;j<i;j++) {
					if(a[j].l>=a[i].r||a[j].r<=a[i].l) continue;
					mx=max(mx,a[j].u);		//在底下的上高最高的长方体
				}
				if(mx==-1) {
					if(a[i].d)flag=true;
					a[i].d=0,a[i].u=a[i].h;
				}
				else if(a[i].d>mx){
					a[i].d=mx,a[i].u=mx+a[i].h;flag=true;
				}
			}
			//2
			sort(a+1,a+1+n,cmp2);
			for(int i=1;i<=n;i++) {
				int mx=-1;
				for(int j=1;j<i;j++) {
					if(a[j].u<=a[i].d||a[j].d>=a[i].u) continue;
					mx=max(mx,a[j].r);
				}
				if(mx==-1) {
					if(a[i].l) flag=true;
					a[i].l=0,a[i].r=a[i].w;
				}
				else if(a[i].l>mx) {
					a[i].l=mx,a[i].r=mx+a[i].w;flag=true;
				}
			}
//			for(int i=1;i<=n;i++) {
//				printf("(%d:) l=%d r=%d u=%d d=%d\n",i,a[i].l,a[i].r,a[i].u,a[i].d);
//			}
//		}
		}while(flag);
		int ansh=0,answ=0;
		for(int i=1;i<=n;i++) {
			ansh=max(ansh,a[i].u),answ=max(answ,a[i].r);
		} 
		printf("%d %d\n",answ,ansh);
	}
	return 0;
}

3.Vasya and a Tree

  • 题意:一棵树,q个操作,每个将x的子树中距离它不超过d的点更新。
  • 思路:我一开始英文不好把不超过看成了等于,发现能用明显的dfs序做。
    然后正解其实很巧妙,利用dfs的顺序,我们把操作用结构体+vector存在点里面,然后dfs存一个sum遇到新点就sum+val,而且打一个标记使得d层之后将sum-val,但注意的是回溯的时候要清空标记。
  • 代码:
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
typedef long long ll;
ll Lazy[N],a[N];
int m,nxt[N],dep[N],to[N],head[N],ecnt,n;
struct node {
	int D;ll val;
};
vector<node> V[N];
void add_edge(int u,int v) {nxt[++ecnt]=head[u];to[ecnt]=v;head[u]=ecnt;}
void dfs(int u,int fa,ll sum) {
	dep[u]=dep[fa]+1;
	sum-=Lazy[dep[u]];
	for(int i=0;i<V[u].size();i++) {
		int Ad=V[u][i].D,v=V[u][i].val;
		Lazy[min(dep[u]+Ad,n)+1]+=v,sum+=v;
	}
	a[u]=sum;
	for(int i=head[u];i;i=nxt[i]) {
		int v=to[i];
		if(v==fa)continue;
		dfs(v,u,sum);
	}
	for(int i=0;i<V[u].size();i++) {
		int Ad=V[u][i].D,v=V[u][i].val;
		Lazy[min(dep[u]+Ad,n)+1]-=v;
	}
}
int main() {
	scanf("%d",&n);
	for(int i=1;i<n;i++) {
		int u,v;
		scanf("%d%d",&u,&v);
		add_edge(u,v),add_edge(v,u);
	}
	scanf("%d",&m);
	for(int i=1;i<=m;i++) {
		int x,d,v;
		scanf("%d%d%d",&x,&d,&v);
		V[x].push_back(node{d,v});
	}
	dfs(1,0,0);
	for(int i=1;i<=n;i++) printf("%lld ",a[i]);
	return 0;
}

4.Regular Number

  • 题意:字符串匹配问题,模式串每一位可能有多种填法。
  • 思路:shift&算法。
    下列变量用bitset维护
    用c[i]表示i的值可以在模式串哪些位出现的状态,dp第i位表示当前有连续i位能匹配上,每新加的0位为1因为只有一位的情况可以直接放入。
  • 代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+5;
const int M=5e6+5;
bitset<N>c[N],dp;
char s[M];
string ans;
int main() {
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++) {
		int x,cc;
		scanf("%d",&cc);
		for(int j=1;j<=cc;j++) {
			scanf("%d",&x);
			c[x][i]=1;
		}
	}
	scanf("%s",s);
	for(int i=0,sz=strlen(s);i<sz;i++) {
		(dp<<=1)[0]=1;
		dp&=c[s[i]-'0'];
		if(dp[n-1]) {
			for(int j=i-n+1;j<=i;j++) {
				ans+=s[j];
			}
			ans+='\n';
		}
	}	
	printf("%s",ans.c_str());
	return 0;
}