2022牛客暑期第二场个人题解

K

简单dp题,签到。
看数据范围大概猜到三维dp,然后记b的前几位,a的最长公共子序列,还有一维记怎么样才合法,记录左括号和右括号的差值。

#include <bits/stdc++.h>
using namespace std;
#define ll long long 
#define endl '\n'
int mod=1000000007;
char s[2005];
ll dp[201][201][201];
void solve() {
	memset(dp,0,sizeof(dp));
	int n,m;
	cin>>n>>m;
	string s;
	cin>>s;
	dp[0][0][0]=1;
	for(int i=1;i<=m;i++) {
		for(int j=0;j<=i;j++) {
			dp[i][0][j] += (j-1<0 ? 0 : dp[i-1][0][j-1]) + dp[i-1][0][j+1];
			dp[i][0][j]%=mod;
		}
	}
	for(int i=1;i<=m;i++) {
		for(int j=1;j<=n;j++) {
			for(int k=0;k<=i;k++){
				int z = -1+2*(s[j-1]=='(');
				if(k-z>=0) dp[i][j][k] += dp[i-1][j-1][k-z];
				if(k+z<=m) dp[i][j][k] += dp[i-1][j][k+z]; 
				dp[i][j][k] %= mod;
			}
		}
	}
	cout<<dp[m][n][0]<<endl;
}
int main() {
	int t;
	cin>>t;
	while(t--) {	
		solve();
	}
} 

J

最小二乘题,可以套用公式,也可以用三元法,套公式的话要考虑到精度的问题,需要换一种更好的公式。

#include <cstdio>
#include <cctype>
#define MN 100000

using ll = __int128;
using ld = long double;

namespace GTI
{
	char gc(void)
   	{
		const int S = 1 << 16;
		static char buf[S], *s = buf, *t = buf;
		if (s == t) t = buf + fread(s = buf, 1, S, stdin);
		if (s == t) return EOF;
		return *s++;
	}
	ll gti(void)
   	{
		ll a = 0, b = 1, c = gc();
		for (; !isdigit(c); c = gc()) b ^= (c == '-');
		for (; isdigit(c); c = gc()) a = a * 10 + c - '0';
		return b ? a : -a;
	}
	int gts(char *s)
   	{
		int len = 0, c = gc();
		for (; isspace(c); c = gc());
		for (; c != EOF && !isspace(c); c = gc()) s[len++] = c;
		s[len] = 0;
		return len;
	}
	int gtl(char *s)
   	{
		int len = 0, c = gc();
		for (; isspace(c); c = gc());
		for (; c != EOF && c != '\n'; c = gc()) s[len++] = c;
		s[len] = 0;
		return len;
	}
}
using GTI::gti;
using GTI::gts;
using GTI::gtl;

int a[MN+5];

void solve(){
	int n;
	ll s_xy=0,s_x=0,s_y=0,s_x2=0;
	n = gti();
	for(int i=1;i<=n;i++){
		a[i] = gti();
		s_xy += (ll)i*a[i];
		s_x += i;
		s_y += a[i];
		s_x2 += (ll)i*i; 
	}
	ld k = (ld)(n*s_xy-s_x*s_y)/(n*s_x2-(s_x)*(s_x));
	ld b = ((ld)s_y/n)-k*((ld)s_x/n);
	ld ans = 0;
	for(int i=1;i<=n;i++){
		ld y = k*i+b;
		ans += (y-a[i])*(y-a[i]);
	}
	printf("%.15Lf\n",ans);
}

int main(){
	int T;
	T = gti();
	while(T--) solve();
}

G

写一下赛时做法,生成一到n的全排列,大小小于10,用next_permutation,然后套一个最长上升,下降子序列,然后记录长度输出最长的,打表找规律,找到规律直接ac。

#include <bits/stdc++.h>
using namespace std;

int t,a[1000006];
void solve () {
	int n;
	cin>>n;
	int num=0; 
	for(int i=1;i*i<=n;i++) num=i;
	if(num*num<n) num+=1;
	int z=1;
	for(int i=n-num;i>=0;i-=num) {
		for(int j=1;j<=num;j++) {
			a[i+j]=z;
			z++;
		}
	}
	if(n%num!=0) {
		int q=n%num;
		int p=n-(n%num);
		for(int i=n;i>=p+1;i--) {
			a[q]=i;
			q--;
		}
	} 
	for(int i=1;i<=n;i++) cout<<a[i]<<' ';
	cout<<endl;
}
int main(){
	int t;
	cin>>t;
	while(t--) {
		solve();
	}
	return 0;
}

D

理解的话,就像我的世界刷物品一样?应该是。
然后很容易想到建图,有一个环就代表可以循环造一种物品。
考虑做一个d物品需要a/c个b物品,设置边权值为a/c,当环上的权值乘w然后累加,结果必须大于1。
w的值考虑二分答案,这是一个最大值最小的问题。

std代码封装得比较好,这里直接贴上。之后再放上自己写的吧,咕咕咕了。
#include <cstdio>
#include <cmath>
#include <queue>
#include <vector>
#define MN 1000

using std::log;
using std::queue;
using std::vector;

using ld = long double;

struct Edge{
	int v;
	ld w;
};

int n,m;
int din[MN+5];
vector<Edge> e[MN+5];
ld psw;

void addEdge(int u,int v,ld w){
	e[u].push_back({v,w});
	din[v]++;
}

struct Dis{
	ld dis;
	int cnt;
	
	void reset(){
		dis = 0;
		cnt = 0;
	}
	
	void setInf(){
		dis = 1e100;
		cnt = 0;
	}
	
	bool operator < (const Dis& that)const{
		return dis+cnt*psw < that.dis+that.cnt*psw;
	}
	
	Dis operator + (ld w)const{
		return {dis+w,cnt+1};
	}
	
};

Dis dis[MN+5];
bool inq[MN+5];

bool hasNegativeLoop(){
	queue<int> q;
	for(int i=1;i<=n;i++){
		dis[i].reset();
		q.push(i);
		inq[i] = true;
	}
	while(!q.empty()){
		int u = q.front();
		q.pop();
		inq[u] = false;
		if(dis[u].cnt>=n) return true;
		for(Edge edge:e[u]){
			int v = edge.v;
			ld w = edge.w;
			if(dis[u]+w<dis[v]){
				dis[v] = dis[u]+w;
				if(!inq[v]){
					q.push(v);
					inq[v] = true;
				}
			}
		}
	}
	return false;
}

bool check(ld psw){
	::psw = psw;
	return !hasNegativeLoop();
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int a,u,c,v;
		scanf("%d%d%d%d",&a,&u,&c,&v);
		ld w = -log((ld)c/a);
		addEdge(u,v,w);
	}
	ld l=0,r=1;
	for(int t=0;t<60;t++){
		ld mid = (l+r)/2;
		if(check(-log(mid))){
			l = mid;
		}else{
			r = mid;
		}
	}
	printf("%.10f\n",(double)r);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值