Codeforces Round #635 div2 Solution

A A

b , c , c b,c,c 一定能构成 \triangle

int a,b,c,d,T;
int main() {
	qr(T); while(T--) {
		qr(a); qr(b); qr(c); qr(d);
		pr1(b); pr1(c); pr2(c);
	}
	return 0;
}

B B

暴力用操作1,再用操作2.

注意操作1不要增即可.

int T,x,n,m;

int main() {
	qr(T); while(T--) {
		qr(x); qr(n); qr(m);
		while(n&&x>(x/2)+10) n--,x=x/2+10;
		if(m*10>=x) puts("YES");
		else puts("NO");
	}
	return 0;
}

C C

最优情况下,一个点被设为工业城市当且仅当子树内所有点都为工业城市.(微扰证明(交换))

所以用堆存 d e p s z dep-sz 即可.

考试的时候想到了,但是不知道为啥打挂了(WA#6)

priority_queue<int>q;
int n,m,fa[N],dep[N],sz[N];
ll ans;
struct edge{int y,next;}a[N<<1]; int len,last[N];
void ins(int x,int y) {a[++len]=(edge){y,last[x]};last[x]=len;}

void dfs(int x) {
	for(int k=last[x],y;k;k=a[k].next)
		if((y=a[k].y)^fa[x]) {
			fa[y]=x;
			dep[y]=dep[x]+1;
			dfs(y);
			sz[x]+=sz[y];
		}
	q.push(dep[x]-sz[x]);
	sz[x]++;
}

int main() {
	qr(n); qr(m); 
	for(int i=1,x,y;i<n;i++)
		qr(x),qr(y),ins(x,y),ins(y,x);
	dfs(1);
	while(m--) ans+=q.top(),q.pop();
	pr2(ans);	
	return 0;
}

D D

有点思维难度.

有个简单性质.

对于确定的 ( x , y ) (x,y) ,那么 z z ( x + y ) / 2 (x+y)/2 越远,总代价越大.

也就是我们取中间的决策优于取两边的决策.(决策包容性)

所以我们可以枚举所有两端为 ( x , y ) , ( x , z ) , ( y , z ) (x,y),(x,z),(y,z) 的情况,然后用二分找到中间位置即可.

int T,a[N],b[N],c[N],n,m,q;
ull ans;

void upd(ll x,ll y,ll z) {
	ans=min(ans,(ull)(x-y)*(x-y)+(x-z)*(x-z)+(y-z)*(y-z));
}

int main() {
	a[0]=b[0]=c[0]=-1e9;
	qr(T); while(T--) {
		qr(n); qr(m); qr(q);
		for(int i=1;i<=n;i++) qr(a[i]);
		sort(a+1,a+n+1);
		for(int j=1;j<=m;j++) qr(b[j]);
		sort(b+1,b+m+1);
		for(int i=1;i<=q;i++) qr(c[i]);
		sort(c+1,c+q+1);
		a[n+1]=b[m+1]=c[q+1]=2e9;//注意边界 
		ans=-1;
		int x=1,y=1;
		while(x<=n) {//x,y
			while(y<m&&b[y]<=a[x]) y++;
			for(int t=-1,*p;t<=0;t++)
				upd(a[x],b[y+t],*(p=lower_bound(c+1,c+q+2,(a[x]+b[y+t])/2))),
				upd(a[x],b[y+t],*(--p));
			x++;
		}
		x=y=1;
		while(x<=n) {//x,z
			while(y<q&&c[y]<=a[x]) y++;
			for(int t=-1,*p;t<=0;t++) 
				upd(a[x],c[y+t],*(p=lower_bound(b+1,b+m+2,(a[x]+c[y+t])/2))),
				upd(a[x],c[y+t],*(--p));
			x++;
		}
		x=y=1;
		while(x<=m) {//y,z
			while(y<q&&c[y]<=b[x]) y++;
			for(int t=-1,*p;t<=0;t++) 
				upd(b[x],c[y+t],*(p=lower_bound(a+1,a+n+2,(b[x]+c[y+t])/2))),
				upd(b[x],c[y+t],*(--p));
			x++;
		}
		pr2(ans);
	}
	return 0;
}

E E

考试的时候一眼看出要用 O ( n 2 ) O(n^2) 做,可是呢…

题目要求我们用 s s 的某个前缀通过操作得到一个前缀为 t t 的字符串.( n = s , m = t n=|s|,m=|t| )

假设我们已经表达出了 t [ i . . j ] t[i..j] ,那么如果 [ s [ j i + 2 ] = t [ i 1 ] ] [ s [ j i + 2 ] = t [ j + 1 ] ] [s[j-i+2]=t[i-1]]\cup [s[j-i+2]=t[j+1]] ,就可表达出 t [ ( i 1 ) . . j ] t [ i . . . ( j + 1 ) ] t[(i-1)..j]或者t[i...(j+1)]

所以很明显这是个区间 d p dp .特别地, t [ ( m + 1 ) . . n ] t[(m+1)..n] 可以匹配任意字符.因为我们只在乎 t [ 1... m ] t[1...m] 完成匹配即可.

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=3010,mod= 998244353;

void upd(int &x) {x-=mod; x+=x>>31&mod;}//x的合法范围[0,2*mod)

int n,m,f[N][N],i,j,ans;
//f[i][j]表示t[i...j]由s[1...(j-i+1)]弄出的方案数.特别地,对于t[m]之后的字符可以匹配一切s中的字符.(我们只在乎前缀匹配) 
char s[N],t[N];

int main() {
	scanf("%s %s",s+1,t+1);
	n=strlen(s+1); 
	m=strlen(t+1);
	for(i=1;i<=m;i++) f[i][i]=(t[i]==s[1])*2;
	for(	;i<=n;i++) f[i][i]=2;
	for(i=2;i<=n;i++) {//枚举长度
		for(j=1;j+i-1<=n;j++)
			upd(f[j][j+i-1]=f[j][j+i-2]*(j+i-1>m||t[j+i-1]==s[i])+f[j+1][j+i-1]*(j>m||t[j]==s[i]));
	}
	for(i=m;i<=n;i++) upd(ans+=f[1][i]);
	printf("%d\n",ans); return 0;
}

F F

如果 2 n 2n 次以上才判WA的话,那么我应该可以在比赛时AC.

2 n 2n 次操作的错误代码:(想看就看吧)

int n,a[N],f[N];
ll l1,l2,n1,n2;

int main() {
	qr(n); scanf("%lld %lld",&l1,&l2);
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=2;j++) {
			printf("+ %d\n",i);
			fflush(stdout);
			scanf("%lld %lld",&n1,&n2);
		}
		a[i]=sqrt(n1-l1);
		l1=n1; l2=n2;
	}
	printf("! ");
	for(int i=1;i<n;i++) printf("%d ",a[i]);
	printf("%d",a[n]); fflush(stdout);
	return 0;
}

正解,EA的代码

我们之所以需要 2 n 2n 次操作,是因为我们没有利用的 s t r a i g h t   t r i p l e t straight ~triplet 的条件.

观察 s t r a i g h t   t r i p l e t ( S T ) straight ~triplet(ST) 的变化:

如果有连续的5个数的出现次数 a , b , c , d , e a,b,c,d,e

增加一个 c c 对应的数,则 Δ S T = a b + b d + d e \Delta ST=ab+bd+de

所以如果我们已知 b , c , d , e b,c,d,e 那么就可以用一次操作求 a a 了.

知道这个之后,我们的突破口就是就是把 c c 逼到角落( n n ).使得参数变少,解出确定的值.

只需两次操作,即可求得 a n a_n 的具体值.然后我们尝试推出前面的数.

因为这次牺牲了两次操作,我们自然想到把剩下的操作分配给 [ 2 , n 1 ] [2,n-1] .(什么鬼,哪自然了)

同时,假如最后取两次 n n 的话, S T ST 的值是相等的,我们是缺乏条件解出 a n 1 a_{n-1} 的.

所以我们考虑设计最后三次为 n , n 1 , n n,n-1,n .

剩下的自己推一下吧~~

这题貌似:

思维难度:省选-

代码难度:普及—

#include<cstdio>
using namespace std;
const int N=110;
int n,l1,l2,t[N],s[N],a,b,ans[N];
void q(int i,int &t,int &s) {
	printf("+ %d\n",i);
	fflush(stdout);
	scanf("%d %d",&t,&s);
	t-=l1; s-=l2;
	l1+=t; l2+=s;
}
int Sqrt(int x) {
	int i=1;
	while(i*i<=x) i++;
	return i-1;
}
int main() {
	scanf("%d %d %d",&n,&l1,&l2);
	for(int i=2;i<=n-2;i++) q(i,t[i],s[i]);
	q(n,t[n],s[n]); q(n-1,t[n-1],s[n-1]); q(n,a,b);
	ans[n]=Sqrt(a+t[n]);
	ans[n-2]=b-s[n]-1;
	ans[n-1]=s[n]/(ans[n-2]+1);
	ans[n-3]=s[n-1]/(ans[n-2]+1)-ans[n]-2;
	for(int i=n-4; i;i--)
		ans[i]=(s[i+2]-ans[i+3]*ans[i+4]-(ans[i+1]+1)*ans[i+3])/(ans[i+1]+1)-1;
	printf("! "); ans[1]++; for(int i=1;i<=n;i++) printf("%d ",ans[i]); return 0;
}

EA牛逼.

总结

这次比赛暴露出对贪心的生疏,和区间DP的不足,需要加强练习.

猜你喜欢

转载自blog.csdn.net/qq_42886072/article/details/105553251