首页 > 其他分享 >MX-NOIP 2024 模拟 3.5

MX-NOIP 2024 模拟 3.5

时间:2024-09-24 08:53:14浏览次数:1  
标签:cnt return int max times 2024 3.5 double MX

赠的场次,质量却很高。

#3.5

T1 交换

连状压都打的复杂度超劣,真是水平下降严重。

其实也基本想到了,前面一大部分贪心确定,后面的做部分分状压 dp。

设 \(f_s\) 表示填了 \(s\) 集合,最优的 \(n'\),\(g_s\) 表示此时对应的 \(n\)。

枚举最高位填哪个数,转移比较简单。

往前换的最大代价只有 \(n \times k \le 10^{17}\) ,而如果换到的位置比 18,19 还要高,那么显然一定会换。

因此最多只有后 \(19\) 位是不确定的,前面一定是前 \(n-19\) 最大值。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10,M=19;
typedef pair<int,int> PII;
PII q[N];
int ans[N]; 
char s[N];
int n,m,k;
int f[1<<M],g[1<<M];
int pw[M+5];

signed main()
{
	freopen("swap.in","r",stdin);
	freopen("swap.out","w",stdout);
	
	cin>>(s)>>k;
	n=strlen(s);
	reverse(s,s+n);
	for(int i=0;i<n;i++)
	{
		int x=s[i]-'0';
		q[i]={x,i};
	}
	
	m=min(n,19ll);
	
	sort(q,q+n);
	
	for(int i=m;i<n;i++) ans[i]=q[i].first;
	
	for(int i=0;i<m;i++) swap(q[i].first,q[i].second);
	sort(q,q+m);
	
	pw[0]=1;
	for(int i=1;i<=m;i++) pw[i]=pw[i-1]*10;
	
	memset(f,-0x3f,sizeof f);
	f[0]=0;
	for(int s=0;s<(1<<m);s++)
	{
		int p=__builtin_popcount(s);
		for(int i=0;i<m;i++)
		{
			if((s>>i)&1) continue;
			int t=s|(1<<i); 
			int v=__builtin_popcount((s>>i));
			int nf=f[s]+q[i].second*pw[p]-v*k,ng=g[s]+q[i].second*pw[p];
			if(f[t]<nf) f[t]=nf,g[t]=ng;
			else if(f[t]<=nf) f[t]=nf,g[t]=ng;
		}
	}
	int x=g[(1<<m)-1];
	for(int i=0;i<m;i++) ans[i]=x%10,x/=10;
	
	for(int i=n-1;i>=0;i--) cout<<ans[i];
	cout<<"\n";
	
	return 0;
}

T2 字符串

为啥我比赛的时候一点不会啊,该加训期望最值问题了。

一直想的是每个串独立?其实根本不是。

先考虑两个串,一位一位填,如果这一位两个串都一样那显然只能是某一个,然后第一个不一样的位置上,选啥都行,之后必须都和某个串的后缀相同。

总共只能错 \(1\) 次,因此是 \(p^{2\times m-1}\)

然后考虑能不能状压拿下 60pts。

看到这个和 LCP 有关系想到 Trie。

建出来虚树,这样结点数就正确了。

设 \(f_{u,s}\) 表示以 \(u\) 为根的子树,\(s\) 集合已经填上。

  • 如果不是叶子:\(f_{u,s}= \max\{p\times f_{ls,s}+(1-p)\times f_{rs,s},(1-p) \times ? + p \times ?\}\)

  • 如果是叶子:当前集合内已经有了这个元素,那就寄了,\(f_{u,s}=0\),否则,向集合中加入这个东西,返回根结点进行计算 \(f_{1,s+\{u\}}\)

考虑这个状压在干什么:对于每个 Trie 的点,如果往左走,必须保证左儿子内部还有没访问到的,往右走同理。

因此对于每个节点,他是正确的必须要保证恰好向左儿子走了 \(k_1\) 次,右儿子走了 \(k_2\) 次。

设 \(g_{a,b}\) 表示向左走了 \(a\) 次,向右走了 \(b\) 次,最大胜率。

枚举最后一步使用更大胜率的是向左走还是向右走。

\(g_{a,b}= \max\{(1-p) \times g_{a-1,b} + p \times g_{a,b-1},p \times g_{a-1,b} + p \times g{a,b-1}\}\)。

所有节点对应的 \(g\) 乘起来就是答案。

状压 dp 代码(贺的):

点击查看代码
#include <bits/stdc++.h>
#define double long double
using namespace std;
bool st;
const int N = 1010 * 1010;
int n, m;
double p;
int ch[N][2], cnt = 0, tot = 0, id[N];
int d[N];
int lt[40], rt[40];
int rk[N];
void insert(int x) {
    string s;
    cin >> s;
    int p = 0;
    for (int i = 0; i < m; i++) {
        bool op = s[i] - '0';
        if (!ch[p][op]) {
            ch[p][op] = ++cnt;
            d[cnt] = d[p] + 1;
        }
        p = ch[p][op];
    }
    id[p] = x;
}
int dfs(int x) {
    if (rk[x])
        return rk[x];
    return dfs(ch[x][0] + ch[x][1]);
}
double dp[40][1 << 18];
int w[40];
bitset<(1 << 18)> book[40];
double Pow[1010];
int len[40];
double to(int x, int y) { return Pow[len[y] - len[x] - 1]; }
double dfs(int x, int s) {
    if (x == 1 && s == (1 << n) - 1)
        return 1;
    if (!x)
        return 0;
    if (book[x][s])
        return dp[x][s];
    book[x][s] = 1;
    double ans;
    if (w[x]) {
        if (s >> w[x] - 1 & 1)
            ans = 0;
        else
            ans = dfs(1, s | (1 << w[x] - 1));
    } else
        ans = max(p * dfs(lt[x], s) * to(x, lt[x]) + (1 - p) * dfs(rt[x], s) * to(x, rt[x]),
                  p * dfs(rt[x], s) * to(x, rt[x]) + (1 - p) * dfs(lt[x], s) * to(x, lt[x]));
    return dp[x][s] = ans;
}
bool ed;
signed main() {
    freopen("string.in", "r", stdin);
    freopen("string.out", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin >> n >> m >> p;
    Pow[0] = 1;
    for (int i = 1; i <= m; i++) Pow[i] = Pow[i - 1] * max(p, 1 - p);
    for (int i = 1; i <= n; i++) insert(i);
    for (int i = 0; i <= cnt; i++)
        if (i == 0 || id[i] || ch[i][0] && ch[i][1])
            rk[i] = ++tot;
    for (int i = 0; i <= cnt; i++) {
        if (!rk[i])
            continue;
        w[rk[i]] = id[i];
        len[rk[i]] = d[i];
        if (ch[i][0])
            lt[rk[i]] = dfs(ch[i][0]);
        if (ch[i][1])
            rt[rk[i]] = dfs(ch[i][1]);
    }
    cout << setprecision(20) << fixed << dfs(1, 0);
    return 0;
}

正解代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10,M=N*N;
double f[N][N];
int tr[M][2],sz[M],idx;
char str[N];
int n,m;
double p;

void insert(char s[])
{
	int u=0;
	for(int i=0;s[i];i++)
	{
		int c=s[i]-'0';
		if(!tr[u][c]) tr[u][c]=++idx;
		u=tr[u][c];
		sz[u]++;
	}
}

signed main()
{
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	
	cin>>n>>m>>p;
	f[0][0]=1;
	for(int j=1;j<=n;j++) f[0][j]=f[0][j-1]*max(p,1-p);
	for(int i=1;i<=n;i++)
	{
		f[i][0]=max(p,1-p)*f[i-1][0];
		for(int j=1;j<=n;j++)
		{
			double v1=f[i-1][j]*p+f[i][j-1]*(1-p);
			double v2=f[i][j-1]*p+f[i-1][j]*(1-p);
			f[i][j]=max(v1,v2);
		}	
	} 
	
	for(int i=1;i<=n;i++)
	{
		cin>>str;
		insert(str);
	}
	
	double res=1;
	for(int u=0;u<=idx;u++)
	{
//		printf("%d %d %.3lf\n",sz[tr[u][0]],sz[tr[u][1]],f[sz[tr[u][0]]][sz[tr[u][1]]]);
		res=res*f[sz[tr[u][0]]][sz[tr[u][1]]];
	}
	
	cout<<fixed<<setprecision(12)<<res<<"\n";
	
	return 0;
}

T3 房间

场切啦!!简单集合容斥转哈希维护。

满足题目要求的点对一定满足:

  • 删掉一个就联通了
  • 删掉的一个和起点连通和终点不连通,另一个和起点不连通和终点连通,这两个点联通。

第一个去个重就行了。

然后考虑判断和起点终点联通就是暴力枚举相邻的连通块,然后看一下存不存在。

判断两个点联通第一种情况也是枚举相邻连通块判断有没有交集,第二种是他俩是相邻的两个,这个可以暴力判断。、

然后判断和一个东西集合有交,由于集合大小很小,可以把集合子集全部哈希,然后容斥即可。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
mt19937 rnd(time(0));
const int N=1e3+10,V=N*N;
typedef pair<int,int> PII;
int id[N][N],tot;
char g[N][N];
int p[V];
int n,m;
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};
vector<int> vec;
PII pos[V];
bool inS[V],inT[V];
vector<int> ne[V];
int val[V];
unordered_map<int,int> Map;

bool chk(int x,int y)
{
	if(x<1 || x>n || y<1 || y>m) return false;
	if(g[x][y]=='#') return false;
	return true;
}

int find(int x)
{
	if(p[x]!=x) return p[x]=find(p[x]);
	return p[x];
}

void merge(int x,int y)
{
	int px=find(x),py=find(y);
	if(px==py) return;
	p[px]=py;
}

bool check(int x,int y)
{
	return find(x)==find(y);
}

bool is_ne(int x,int y)
{
	for(auto a:ne[x])
	{
		for(auto b:ne[y])
		{
			if(a==b) return true;
		}
	}
	return false;
}


signed main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	
	freopen("room.in","r",stdin);
	freopen("room.out","w",stdout);
	
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>(g[i]+1);
		for(int j=1;j<=m;j++) 
		{
			id[i][j]=++tot,p[tot]=tot,val[tot]=rnd();
			pos[tot]={i,j};
			if(g[i][j]=='#') vec.push_back(tot);
		}
	}
	
//	for(auto x:vec) cout<<x<<"\n";
	
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(!chk(i,j)) continue;
			for(int d=0;d<4;d++)
			{
				int x=i+dx[d],y=j+dy[d];
				if(!chk(x,y)) continue;
				merge(id[i][j],id[x][y]);
			}
		}
	}
	
	if(check(id[1][1],id[n][m]))
	{
		int sz=vec.size();
		cout<<sz*(sz-1)/2<<"\n";
		return 0; 
	}
	
	for(auto x:vec)
	{
		int i=pos[x].first,j=pos[x].second;
		for(int d=0;d<4;d++)
		{
			int a=i+dx[d],b=j+dy[d];
			if(!chk(a,b)) continue;
			int y=find(id[a][b]);
			ne[x].push_back(y);
			if(y==find(id[1][1])) inS[x]=true;
			if(y==find(id[n][m])) inT[x]=true;
		}
		sort(ne[x].begin(),ne[x].end());
		ne[x].erase(unique(ne[x].begin(),ne[x].end()),ne[x].end());
	}
	
	for(auto x:vec)
	{
		if(inS[x]) continue;
		if(!inT[x]) continue;
		int sz=ne[x].size();
		for(int s=1;s<(1<<sz);s++)
		{
			int w=0;
			for(int j=0;j<sz;j++)
			{
				if((s>>j)&1) w^=val[ne[x][j]];
			}
			Map[w]++;
		}
	}
	
	int res=0;
	for(auto x:vec)
	{
		if(!inS[x]) continue;
		if(inT[x]) continue;
		int sz=ne[x].size();
		int cnt=0;
		for(int s=1;s<(1<<sz);s++)
		{
			int f=(__builtin_popcount(s)&1)?1:-1;
			int w=0;
			for(int j=0;j<sz;j++)
			{
				if((s>>j)&1) w^=val[ne[x][j]]; 
			}
			if(Map.count(w)) cnt+=f*Map[w];
		}
		int a=pos[x].first,b=pos[x].second;
//		if(cnt) printf("[%d %d %d]\n",a,b,cnt);
		for(int u=0;u<4;u++)
		{
			int c=a+dx[u],d=b+dy[u];
			if(c<1 || c>n || d<1 || d>m) continue;
			if(g[c][d]=='.') continue;
			if(inS[id[c][d]]) continue;
			if(!inT[id[c][d]]) continue;
			if(is_ne(x,id[c][d])) continue;
			cnt++;
		}
		res+=cnt;
//		if(cnt) printf("[%d %d %d]\n",a,b,cnt);
	}
	
//	cout<<res<<"\n";
	
	int sz=vec.size(),cnt=0;
	for(auto x:vec)
	{
		if(!inS[x]) continue;
		if(!inT[x]) continue;
		cnt++;
		res+=sz-1;
	}
	
//	cout<<cnt<<"\n";
	
	res-=cnt*(cnt-1)/2;
	
	cout<<res<<"\n";
	
	return 0;
}

T4 电力公司

模拟赛见过很好的题目!!!

没有中途交点这个条件相当于匹配的左端点,右端点都是单调递增的。

然后就转化为在网格图上 dp,状态应该还得压一个二进制串表示当前行是否选了,列是否选了。

本质是分层图最长路。

可以获得 \(40\) 分的高分。

多组询问,考虑对一维分治。

每次处理 \([a,b]\) 跨过 \(mid\) 的询问,它必然在某个条竖线穿过 \(mid\),枚举这个竖线,跑两个单源最长路,枚举询问合并答案即可。

注意这个枚举的中转点要特别注意,他的代价提前算,因为钦定他选了。

他的贡献只能在一边算。

点击查看代码
#include<bits/stdc++.h>
#pragma GCC optimize(2)
#define max(A,B) (A)>(B)?(A):(B)
using namespace std;
const int N=310,M=1e5+10,Inf=1e9;
int f[N][N][3],g[N][N][3];
int ans[N][N][3];
int w[N][N];
int Ans[M];
int u[N],v[N];
struct Node
{
	int a,b,c,d,id;
};
int n,m;

inline void clear(int l1,int r1,int l2,int r2,int tp)
{
	for(int i=l1;i<=r1;i++) for(int j=l2;j<=r2;j++) ans[i][j][tp]=f[i][j][0]=f[i][j][1]=f[i][j][2]=-Inf;
}

inline void Dp1(int l1,int r1,int l2,int r2,int tp)
{
	int res=0;
	f[l1][l2][0]=0,f[l1][l2][2]=0;
	int tmp=w[l1][l2];
	w[l1][l2]=0;
	for(int i=l1;i<=r1;i++)
	{
		for(int j=l2;j<=r2;j++)
		{
			int maxv=-Inf;
			ans[i][j][tp]=max(max(f[i][j][0],f[i][j][1]),f[i][j][2]);
			if(i+1<=r1) 
			{
				f[i+1][j][0]=max(max(f[i][j][0],f[i][j][1]),f[i][j][2]);
				f[i+1][j][2]=f[i][j][2];
			}
			if(j+1<=r2)
			{
				f[i][j+1][0]=max(max(f[i][j][0],f[i][j][1]),f[i][j][2]);
				f[i][j+1][1]=f[i][j][1];
			}
			for(int s=0;s<3;s++)
			{
				int t=3^s,c=((t>>0)&1)*u[i]+((t>>1)&1)*v[j],val=f[i][j][s]-c+w[i][j];
				maxv=max(maxv,val);
			}
			if(i+1<=r1) f[i+1][j][2]=max(f[i+1][j][2],maxv);
			if(j+1<=r2) f[i][j+1][1]=max(f[i][j+1][1],maxv);
			ans[i][j][tp]=max(ans[i][j][tp],maxv);
		}
	}
	w[l1][l2]=tmp;
}

inline void Dp2(int l1,int r1,int l2,int r2,int tp)
{
	f[r1][r2][0]=0,f[r1][r2][2]=0;
	for(int i=r1;i>=l1;i--)
	{
		for(int j=r2;j>=l2;j--)
		{
			int maxv=-Inf;
			ans[i][j][tp]=max(max(f[i][j][0],f[i][j][1]),f[i][j][2]);
			if(i-1>=l1) 
			{
				f[i-1][j][0]=max(max(f[i][j][0],f[i][j][1]),f[i][j][2]);
				f[i-1][j][2]=f[i][j][2];
			}
			if(j-1>=l2)
			{
				f[i][j-1][0]=max(max(f[i][j][0],f[i][j][1]),f[i][j][2]);
				f[i][j-1][1]=f[i][j][1];
			}
			for(int s=0;s<3;s++)
			{
				int t=3^s,c=((t>>0)&1)*u[i]+((t>>1)&1)*v[j],val=f[i][j][s]-c+w[i][j];
				maxv=max(maxv,val);
			}
			if(i-1>=l1) f[i-1][j][2]=max(f[i-1][j][2],maxv);
			if(j-1>=l2) f[i][j-1][1]=max(f[i][j-1][1],maxv);
			ans[i][j][tp]=max(ans[i][j][tp],maxv);
		}
	}
}


inline void solve(int l,int r,vector<Node> &Q)
{
	if(l>r || Q.size()<=0) return;
	vector<Node> L,R,now;
	int mid=l+r>>1; 
	for(auto &it:Q)
	{
		if(it.b<mid) L.push_back(it);
		else if(it.a>mid) R.push_back(it);
		else now.push_back(it);
	}
	if(now.size())
	{
		for(int j=1;j<=n;j++)
		{
			Dp2(l,mid,1,j,2);
			Dp1(mid,r,j,n,1);
			for(auto &it:now)
			{
				int a=it.a,b=it.b,c=it.c,d=it.d,id=it.id;
				if(c<=j && d>=j) 
					Ans[id]=max(Ans[id],ans[a][c][2]+ans[b][d][1]-v[j]); 
			}
			clear(l,mid,1,j,2);
			clear(mid,r,j,n,1);
		}	
	}
	
	if(L.size()) solve(l,mid-1,L);
	if(R.size()) solve(mid+1,r,R);
}

signed main()
{
	freopen("tower.in","r",stdin);
	freopen("tower.out","w",stdout); 
		
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
		
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>u[i];
	for(int i=1;i<=n;i++) cin>>v[i];
	
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			cin>>w[i][j];
	
	vector<Node> vec;
	for(int i=1,a,b,c,d;i<=m;i++)
	{
		cin>>a>>b>>c>>d;
		vec.push_back({a,b,c,d,i});
	}
	clear(1,n,1,n,1),clear(1,n,1,n,2);
	solve(1,n,vec);
	
	for(int i=1;i<=m;i++) cout<<Ans[i]<<"\n";
	
	return 0;
}

标签:cnt,return,int,max,times,2024,3.5,double,MX
From: https://www.cnblogs.com/Richardwhr/p/18428050

相关文章

  • React的useId,现在Vue3.5终于也有了!
    前言React在很早之前的版本中加了useId,用于生成唯一ID。在Vue3.5版本中,终于也有了期待已久的useId。这篇文章来带你搞清楚useId有哪些应用场景,以及他是如何实现的。关注公众号:【前端欧阳】,给自己一个进阶vue的机会useId的作用他的作用也是生成唯一ID,同一个Vue应用里面每次调用......
  • 2024.9.23docker常用命令
    1.容器管理查看运行中的容器:dockerps查看所有容器(包括已停止的):dockerps-a启动容器:dockerstart<container_id或container_name>停止容器:dockerstop<container_id或container_name>重启容器:dockerrestart<container_id或container_name>删除......
  • 【专题】2024AI智慧生活白皮书:AI智能科技重塑居家体验报告合集PDF分享(附原数据表)
    原文链接:https://tecdat.cn/?p=37748AI已然成为家电家居市场的创新核心动力,可在个性化识别、预测维护等多方面提升产品价值。家享生活行业智能化展现多元场景,清洁智能崛起超厨房智能居第二,全屋智能潜力巨大。“套装/集成智能”等品类增长快,智能新客多由老客升级,消费有时序性,......
  • 20240816
    MusicFestival我们设状态为当前的炫酷值为\(i\),则\(dp_i\)表示炫酷值,然后将每个专辑按照最大值排序即可#include<bits/stdc++.h>usingnamespacestd;constintN=2e5+5;structnode{vector<int>a;intmaxi;}x[N];intt,n,k[N],tr[N*4],p[N],c......
  • 14.STM32F103C8T6+CUBEMX+循迹模块(定时器计数、测速)
        学习完b站keysking老师的视频后写了这篇笔记,主要是学习定时器外部时钟。    用的是TIM2(有ETR,TIM3和TIM4都没有)一、cubemx配置(没加无上限计数)1、开TIM2    选了外部时钟模式1,借助从模式ETR1;        下面改数值15是输入滤波器,因为抖动......
  • 2024csps初赛记
    对于此次初赛,教训有不少,有一些差点把自己整死。第一点,铅笔只能用2B,不要尝试使用HB2nd:一定要带涂卡笔和橡皮,不然就算借别人用了也会发现橡皮还不如手擦的干净(可能因为这个原因我都要丢几分)。3rd:这是新的不属于失误的经验,尽管做了多套历年题目,但考试的题目难度可不能用不完全归纳......
  • 2024/9/23日总结
    publicclassEnumTest{publicstaticvoidmain(String[]args){ Sizes=Size.SMALL; Sizet=Size.LARGE; //s和t引用同一个对象? System.out.println(s==t);// //是原始数据类型吗? System.out.println(s.getClass().isPrimitive()); //从字符串中转换 Sizeu=Size.v......
  • NOIP2024集训Day36 DP优化
    NOIP2024集训Day36DP优化A.[NOIP2023]天天爱打卡前段时间才看过这道题。dp+线段树优化+离散化。经典。考虑朴素dp。定义\(f_i\)表示考虑到第\(i\)个位置,并钦定第\(i\)天跑步的最大能量值。枚举最后一段跑步时间,有:\(f_i=\max(\max\limits_{k\ltj}f_k-(i-......
  • 2024最新高分源码基于SpringBoot+Vue+uniapp的大湾区旅游推荐系统(源码+lw+部署文档+
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • 2024最新高分源码基于SpringBoot+Vue+uniapp的物流配送人员车辆调度管理系统(源码+lw+
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......