Comet OJ - Contest #0 题解

版权声明:欢迎随便转载。 https://blog.csdn.net/a1214034447/article/details/89003729

题目链接:https://cometoj.com/contest/34/problems

A.解方程

\sqrt{x - \sqrt{n}} + \sqrt{y} -\sqrt{z} = 0,(由公式可知y>=z,0<=n<=根号x)移项再平方可得x - \sqrt{n} = z - \sqrt{4*y*z} + y

x - (z+y) = \sqrt{n} - \sqrt{4*y*z}

那么如果n是个平方数,很明显解有无穷多个。

否则必须满足 n = 4*y*z才有整数解,即 x = (z+y)。所以只要枚举n/4的所有因子就好了。(注意这样做卡longlong,要想不卡去筛素数吧)

#include <bits/stdc++.h>
using namespace std;
typedef pair <int,int> pa;
typedef long long ll;
const int mod = 1e9 + 7;
const int mx = 1e5 + 10; 
int main() {
	int n;
	int t;scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		ll k = sqrt(n);
		if(k*k==n) puts("infty");
		else{
			if(n%4!=0) puts("0 0");
			else{
				ll ans = 0;
				int ca = 0;
				n /= 4;
				for(int i=1;i*i<=n;i++){
					if(n%i==0){
						ans += 1ll*i*(n/i)%mod*(i+n/i)%mod;
						ans %= mod;
						ca++;
					}
				}
				printf("%d %lld\n",ca,ans);
			}
		}
	} 
	return 0;
}

B.旅途

由题意可知最终操作了m-1次,所以乘上100^(m-1)就保证了数为整数,直接抵消分母。

由题意可知m天恰好游玩i个城市肯定是连续的一段,那么我可以想去dp。

最普通的dp就是dp[q][i][j][k]表示旅游了q天,除了(i,j)区间内城市其他城市都旅游过了,且现在在k城市,转移方程就是在k处两种选择顺时针,逆时针,停留。如果k在i或者j就说明可能旅游新的城市了。

扫描二维码关注公众号,回复: 6072700 查看本文章

但是O(n^4)肯定是过不了的(没想到O(n^3)跑那么快,真得狗)。

所以我们就想办法怎么用<4维的dp去维护四维的变量。首先天数q肯定是一定有的,所以剩下只能缩小后面三个,但是我们仔细想一想k在哪重要吗?重要的是k是否在i或者j上,换句话说判断k和i或者j的距离是否为0就好了,还有一个重要的变量就是得知道旅游了多少个城市,城市个数可以化为k和i的距离+k和j的距离+1。所以最后四维dp就可以优化为三维dp:

dp[i][j][k]表示旅游了i天,现在在的城市距离已经旅游过的城市的左端点j距离,和距离已经旅游过的城市的右端点k距离。

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 505;
const int mod = 1e9 + 7;
int dp[mx][mx][mx];
ll q,p,r,fac[mx];
int main(){
	//freopen("1.in","r",stdin);
	int t,n,m,K;scanf("%d",&t);
	fac[0] = 1;for(int i=1;i<mx;i++) fac[i] = fac[i-1]*100%mod;
	while(t--){
		scanf("%d%d%d%lld%lld",&n,&m,&K,&p,&q);
		r = 100 - p - q;
		dp[1][0][0] = 1;
		for(int i=2;i<=m;i++)
		for(int j=0;j<min(i,n);j++)
		for(int k=0;k+j<min(i,n);k++) dp[i][j][k] = 0;
		
		for(int i=1;i<m;i++){
			int mi = min(i,n);
			for(int j=0;j<mi;j++){
				for(int k=0;k+j<mi;k++){
					if(dp[i][j][k]){
						if(j+k+1==n){
							dp[m][j][k] = (dp[m][j][k] + dp[i][j][k]*fac[m-i]%mod)%mod;
							continue;
						}
						dp[i+1][max(0,j-1)][k+1] = (dp[i+1][max(0,j-1)][k+1]+dp[i][j][k]*q%mod)%mod;
						dp[i+1][j+1][max(0,k-1)] = (dp[i+1][j+1][max(0,k-1)]+dp[i][j][k]*p%mod)%mod;
						dp[i+1][j][k] = (dp[i+1][j][k]+dp[i][j][k]*r%mod)%mod;
					}
				}
			}
		}
		ll ans = 0;
		for(int i=1;i<=n;i++){
			ll mul = 1,ret = 0;
			for(int j=1;j<=K;j++) mul = mul*i%mod;
			for(int j=0;j<i;j++){
				ret = (ret + dp[m][j][i-j-1])%mod;
			}
			ans = (ans+mul*ret%mod)%mod;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

C.项链与计数

还没补

D.战术安排

还是想着中规中矩的DP(数据这么小一直想着怎么去暴力,但是都不成),正因为数据小,所以我们也可以想怎么DP。

dp[i][j][k]表示i二进制位表示6个题目的做与不做的情况下,猫在第j分钟后无事可做和兔在第k分钟后无事可做的情况下象最少在第几分钟后无事可做。

原来一直想着不用状态压缩,因为不用去考虑他们的做题顺序,但是大错特错,他们的做题顺序很明显是有关系的。比如我AB组合做完B就可以再做一题了,但是要是先让AC做,那么AB就被卡住了,使得B无事可做的时间延后了。所以顺序是有关系的。

所以最后的时间复杂度是O(50*2^(6)*180*180*6),虽然复杂度有点高,但是跑起来还行。

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int mx = 1e5 + 10;
int dp[1<<7][200][200];
int a[10][10];
void uptd(int i,int j,int k,int v)
{
	if(max(j,max(k,v))<=180)
	dp[i][j][k] = min(dp[i][j][k],v);
}
int main(){
	//freopen("1.in","r",stdin);
	int t,c,d;scanf("%d",&t);
	while(t--){
		int len  = (1<<6);
		for(int i=0;i<6;i++){
			for(int j=1;j<=7;j++) scanf("%d",a[i]+j);
		}
		memset(dp,inf,sizeof(dp));
		dp[0][0][0] = 0;
		for(int i=0;i<len;i++){
			for(int j=0;j<=180;j++){
				for(int k=0;k<=180;k++){
					int v = dp[i][j][k];
					if(dp[i][j][k]!=inf)
					for(int w=0;w<6;w++){
						if((1<<w)&i) continue;
						int s = (1<<w)|i;
						uptd(s,j+a[w][1],k,v);
						uptd(s,j,k+a[w][2],v);
						uptd(s,j,k,v+a[w][3]);
						c = max(j,k) + a[w][4];
						uptd(s,c,c,v);
						c = max(j,v) + a[w][5];
						uptd(s,c,k,c);
						c = max(k,v) + a[w][6];
						uptd(s,j,c,c);
						c = max(j,max(k,v)) + a[w][7];
						uptd(s,c,c,c);
					}
				}
			}
		}
		int ansp = 0,ansv = 0;
		for(int i=0;i<len;i++){
			for(int j=0;j<=180;j++){
				for(int k=0;k<=180;k++){
					if(dp[i][j][k]!=inf){
						c = max(j,max(k,dp[i][j][k]));
						d = __builtin_popcount(i);
						if(d>ansp||(d==ansp&&ansv>c))
						ansp = d,ansv = c;
					}
				}
			}
		}
		printf("%d %d\n",ansp,ansv);
	}
	return 0;
}

E.最长上升子序列

用pre[i]表示前缀以a[i]结尾的最长上升子序列长度。

用suf[i]表示后缀以a[i]结尾的最长下降子序列长度。

情况1:改变ax可以使得原来最长上升子序列的长度cnt+1。

如果有pre[i]+suf[j] == cnt 且 i<j 且a[i]+1<a[j]。那么就可以再(i,j)区间内任意一个地方变为a[i]+1,就可以cnt+1。

换句话说固定左区间i,寻找最远的j使得可以再(i,j)中插入a[i]+1使得cnt+1。

这个可以用很多方法解决,最直接的方法就是用set。

情况2:不能使得cnt+1,但可以把ax变为0

1.没有ax原序列的最长上升子序列还是cnt。(找到pre[x]==pre[y] 且 pre[y]+suf[y]==cnt+1)

2.ax在原上升子序列的第一个(pre[x]==1),那么显然可以把它变为0。

3.存在x<y 且 suf[y] == cnt - 1。此时可以把ax变为0作为第一个。

情况3:不能使cnt+1,只能不变或更小但不能变为0

实际上这个情况和情况1是一样的,当情况1,2都不存在时,如果有:

如果有pre[i]+suf[j] == cnt-1 且 i<j 且a[i]+1<a[j]。那么就可以再(i,j)区间内任意一个地方变为a[i]+1,如果本来的数大于a[i]+1,那么就可以变小了。且最后的长度还是cnt。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define fi first
#define se second
using namespace std;
typedef pair<int,int> pa;
const int mx = 1e5 + 10;
int a[mx],b[mx],q[mx];
int n,pre[mx],suf[mx];
int pos[2][mx],can[mx];
set <pa> du[2][mx];
multiset <int> s[2];
vector <int> g[2][mx];
bool ok[mx];
void getup(int v,int id,int len){
	for(int i=n;i>=1;i--){
		int w = len - suf[i] - v;
		if(w<0) continue;
		auto it = du[id][w].begin();
		while(it!=du[id][w].end()){
			if(it->fi+1>=a[i]) break;
			if(it->se<i){
				pos[id][it->se] = i;
				g[id][i].push_back(it->fi);
			}
			du[id][w].erase(it++);
		}
	}
}
void init(){
	for(int i=0;i<=n;i++){
		pos[0][i] = pos[1][i] = 0;
		g[0][i].clear();g[1][i].clear();
		du[0][i].clear();du[1][i].clear();
		s[0].clear();s[1].clear();
		can[i] = ok[i] = 0;
	}
}
void solve(int len){
	for(int i=1;i<=n;i++){
		du[1][i] = du[0][i];
		if(suf[i]==len&&a[i]){
			s[0].insert(-1);
			g[0][i].push_back(-1);
		}
		if(suf[i]==len-1&&a[i]){
			s[1].insert(-1);
			g[1][i].push_back(-1);
		}
		if(pre[i]+suf[i]==len+1)
		can[pre[i]]++,ok[i] = 1;
	}
	getup(0,0,len);getup(1,1,len);
	for(int i=1;i<=n;i++){
		for(int j=0;j<2;j++){
			for(int k:g[j][i])
			s[j].erase(s[j].lower_bound(k));
		}
		if(s[0].size()){
			printf("%d %d\n",len+1,*s[0].begin()+1);
		}else{
			if(pre[i]==1||can[pre[i]]>1||!ok[i])
			printf("%d 0\n",len);
			else printf("%d %d\n",len,*s[1].begin()+1);
		}
		if(pos[0][i]||pre[i]==len) s[0].insert(a[i]);
		if(pos[1][i]||pre[i]==len-1) s[1].insert(a[i]);
	}
}
int main(){
	//freopen("1.in","r",stdin);
	int T; scanf("%d",&T);
	while (T--){
		init();
		scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%d",a+i),b[i] = -a[i];
		int k = 1;
		q[0] = a[1],pre[1] = 1;
		du[0][1].insert(pa(a[1],1));
		for(int i=2;i<=n;i++){
			int w = lower_bound(q,q+k,a[i]) - q;
			pre[i] = w+1,q[w] = a[i];
			if(w==k) k++; 
			du[0][pre[i]].insert(pa(a[i],i));
		}
		k = 1,q[0] = b[n],suf[n] = 1;
		for(int i=n-1;i>=1;i--){
			int w = lower_bound(q,q+k,b[i]) - q;
			suf[i] = w+1,q[w] = b[i];
			if(w==k) k++;
		}
		solve(k); 
	}
	return 0;
}

F.木棍与多边形

还没补。

猜你喜欢

转载自blog.csdn.net/a1214034447/article/details/89003729