zoukankan      html  css  js  c++  java
  • 牛客练习赛89 题解

    前言

    比赛链接:https://ac.nowcoder.com/acm/contest/11179#question
    比赛开始后 (1) 小时才注意到。看了一下榜感觉过 (4) 题就能不掉,于是直接开了 D 题。发现会做,然后就打了。
    结果第一次 AK 了练习赛。这不水一篇博客 2333。

    A 牛牛吃米粒

    题目链接:https://ac.nowcoder.com/acm/contest/11179/A

    直接把 (s) 转化成二进制数就好了。

    #include <bits/stdc++.h>
    using namespace std;
    typedef unsigned long long ull;
    
    int n,k;
    ull s,x;
    
    int main()
    {
    	cin>>n>>k>>s;
    	for (int i=1;i<=k;i++)
    	{
    		cin>>x;
    		if (x==64) continue;
    		if (s&(1ULL<<(x-1))) return printf("NO"),0;
    	}
    	printf("YES");
    	return 0;
    }
    

    B 牛牛嚯可乐

    题目链接:https://ac.nowcoder.com/acm/contest/11179/B

    长度很小。直接爆搜。可能有更简单的做法。这种无脑我就直接写了。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=10;
    const char t[]={'00','c','o','c','a','c','o','l','a'};
    int ans;
    char s[N];
    
    void dfs(int x,int cnt)
    {
    	if (x>8) { ans=min(ans,cnt); return; }
    	if (s[x]==t[x]) dfs(x+1,cnt);
    	else
    	{
    		for (int i=x+1;i<=8;i++)
    			if (s[i]==t[x])
    			{
    				swap(s[i],s[x]);
    				dfs(x+1,cnt+1);
    				swap(s[i],s[x]);
    			}
    	}
    }
    
    int main()
    {
    	scanf("%s",s+1);
    	ans=1e9; dfs(1,0);
    	cout<<ans;
    	return 0;
    }
    

    C 牛牛吃豆人

    题目链接:https://ac.nowcoder.com/acm/contest/11179/C

    也就是找两条路径要求在起点终点外不交。
    显然两条路径必然是 ((1,1) o (2,1) o cdots o (n,2) o (n,3))((1,1) o (1,2) o cdots o (n-1,3) o (n,3))。因为每一列至少有一个障碍,所以只需要看转折的时候会不会出问题。
    也就是第一列最上的障碍必须比第二列最下的障碍至少低 (2) 行;第三列最下的障碍必须比第二列最上的障碍至少高两行。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=1000010;
    int n,m,p1,p2,p3,p4;
    
    int main()
    {
    	scanf("%d%d",&n,&m);
    	p1=1e9; p2=1e9; p3=0; p4=0;
    	for (int i=1,x,y;i<=m;i++)
    	{
    		scanf("%d%d",&x,&y);
    		if (x==1) p1=min(p1,y);
    		if (x==2) p2=min(p2,y),p3=max(p3,y);
    		if (x==3) p4=max(p4,y);
    	}
    	if (p1>p3+1 && p4<p2-1) cout<<"YES";
    		else cout<<"NO";
    	return 0;
    }
    

    D 牛牛种小树

    题目链接:https://ac.nowcoder.com/acm/contest/11179/D

    首先除了根节点以外所有点都恰好有一个父亲,忽略掉这个度数后,只需要考虑每一个点往儿子的边的度数。
    把根节点单独处理,就是将 (n-1) 个物品分为 (n-1) 堆,大小为 (x) 的一堆的价值为 (f(x+1))
    这个直接完全背包搞就行了。但是注意如果不足 (n-1) 堆,需要凑大小为 (0) 的堆,而大小为 (0) 的堆的价值并不是 (0),所以考虑贡献提前计算,也就是一开始就设价值为 ((n-1) imes f(1)),然后每加入一堆时贡献都需要减去 (f(1))
    时间复杂度 (O(n^2))

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=10010;
    int n;
    ll ans,f[N],g[N],v[N];
    
    int main()
    {
    	scanf("%d",&n);
    	for (int i=1;i<n;i++)
    		scanf("%lld",&v[i]);
    	memset(f,0xcf,sizeof(f));
    	f[0]=v[1]*(n-1);
    	for (int i=1;i<n;i++)
    		for (int j=i;j<n;j++)
    			f[j]=max(f[j],f[j-i]+v[i+1]-v[1]);
    	for (int i=1;i<n;i++)
    		ans=max(ans,f[n-1-i]+v[i]);
    	cout<<ans;
    	return 0;
    }
    

    E 牛牛小数点

    题目链接:https://ac.nowcoder.com/acm/contest/11179/E

    手玩一下会发现,对于一个数 (x=2^{a} imes 5^{b} imes p)

    • 如果 (p=1),那么这是一个有限小数,贡献为 (0)
    • 如果 (p>1),那么贡献为 (max(a,b)+1)

    把每一次询问拆成两个前缀和相减,对于 (p=1) 的部分,可以最后暴力找出每一个符合的数减去贡献。所以可以忽略掉 (p) 的限制。
    考虑贡献至少(k) 的数有几个。也就是质因数分解后,(max(a,b)+1geq k) 的数量。很显然是 (lfloorfrac{n}{2^k} floor+lfloorfrac{n}{5^k} floor-lfloorfrac{n}{10^k} floor)
    那么枚举 (k) 计算一下就好了。

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int MOD=998244353;
    int Q;
    ll l,r;
    
    ll solve(ll n)
    {
    	ll ans=0;
    	ll res1=1,res2=1,res3=1;
    	for (int i=1;i<=50;i++)
    	{
    		if (res1<=n) ans=(ans+n/res1)%MOD,res1*=2LL;
    		if (res2<=n) ans=(ans+n/res2)%MOD,res2*=5LL;
    		if (res3<=n) ans=(ans-n/res3)%MOD,res3*=10LL;
    	}
    	res1=1;
    	for (int i=0;res1<=n;i++)
    	{
    		res2=res1;
    		for (int j=0;res2<=n;j++)
    			ans=(ans-max(i,j)-1)%MOD,res2=res2*5LL;
    		res1*=2LL;
    	}
    	return (ans%MOD+MOD)%MOD;
    }
    
    int main()
    {
    	scanf("%d",&Q);
    	while (Q--)
    	{
    		scanf("%lld%lld",&l,&r);
    		cout<<(solve(r)-solve(l-1)+MOD)%MOD<<"
    ";
    	}
    	return 0;
    } 
    

    F 牛牛防疫情

    题目链接:https://ac.nowcoder.com/acm/contest/11179/F

    一眼看上去就觉得很网络流。
    发现每一个格子要么被感染,要么被墙隔离。观察到如果两个格子之间有墙,当且仅当这两个格子一个是被感染,一个不被感染。
    那么这个模型就十分的像最大权闭合子图了。源点连向每一个感染源,流量为 (+infty);相邻格子之间连流量为 (1);所有格子向 (T) 连流量为 (c)
    这样如果一个格子最后不被感染,那么与相邻的被感染的格子之间的边就需要割掉;如果被感染,那么与 (T) 之间的边就需要割掉。
    最后答案再减去初始 (m) 个感染源的代价即可。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=2000010,Inf=1e9;
    const int dx[]={0,0,0,-1,1},dy[]={0,-1,1,0,0};
    int n,m,c,S,T,maxf,tot=1,head[N],cur[N],dep[N];
    
    int ID(int x,int y)
    {
    	return n*(x-1)+y;
    }
    
    struct edge
    {
    	int next,to,flow;
    }e[N];
    
    void add(int from,int to,int flow)
    {
    	e[++tot]=(edge){head[from],to,flow};
    	head[from]=tot;
    	swap(to,from);
    	e[++tot]=(edge){head[from],to,0};
    	head[from]=tot;
    }
    
    bool bfs()
    {
    	memset(dep,0x3f3f3f3f,sizeof(dep));
    	memcpy(cur,head,sizeof(head));
    	queue<int> q;
    	q.push(S); dep[S]=0;
    	while (q.size())
    	{
    		int u=q.front(); q.pop();
    		for (int i=head[u];~i;i=e[i].next)
    		{
    			int v=e[i].to;
    			if (e[i].flow && dep[v]>dep[u]+1)
    			{
    				dep[v]=dep[u]+1;
    				q.push(v);
    			}
    		}
    	}
    	return dep[T]<Inf;
    }
    
    int dfs(int x,int flow)
    {
    	if (x==T) return flow;
    	int used=0,res;
    	for (int i=cur[x];~i;i=e[i].next)
    	{
    		int v=e[i].to; cur[x]=i;
    		if (e[i].flow && dep[v]==dep[x]+1)
    		{
    			res=dfs(v,min(flow-used,e[i].flow));
    			e[i].flow-=res; e[i^1].flow+=res; used+=res;
    			if (used==flow) return used;
    		}
    	}
    	return used;
    }
    
    void dinic()
    {
    	while (bfs()) maxf+=dfs(S,Inf);
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	scanf("%d%d%d",&n,&m,&c);
    	S=N-1; T=N-2;
    	for (int i=1,x,y;i<=m;i++)
    	{
    		scanf("%d%d",&x,&y);
    		x++; y++;
    		add(S,ID(x,y),Inf);
    	}
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=n;j++)
    		{
    			add(ID(i,j),T,c);
    			for (int k=1;k<=4;k++)
    			{
    				int x=i+dx[k],y=j+dy[k];
    				if (x>=1 && x<=n && y>=1 && y<=n) add(ID(i,j),ID(x,y),1);
    			}
    		}
    	dinic();
    	cout<<maxf-c*m;
    	return 0;
    }
    
  • 相关阅读:
    统计一个字符串中字母、空格和数字的个数
    java 将一个数组中的值按逆序重新存放,例如,原来顺序为:9,5,7,4,8,要求改为:8,4,7, 5,9。
    java判断一个数是否为素数[转]
    Set集合
    List&ArrayList&LinkedList
    java_异常
    内部类&匿名内部类
    多态&抽象类&接口
    数组排序和字符串
    Java笔记_数据类型和运算符
  • 原文地址:https://www.cnblogs.com/stoorz/p/15332839.html
Copyright © 2011-2022 走看看