SDNU_ACM_ICPC_2022_Winter_Practice_4th(补题)

A - A Funny Game(对称博弈)

A,B两人轮流从摆成一个圆的n个硬币中取硬币,可以取1个也可以取相邻的两个,最后谁无法取了算输,给出n,A先手,问谁最后会赢。

思路: 对称博弈。对于n<3,A必胜,但是n>=3时,A取过之后B都有办法取一定的数目来构造出中心对称,这样可以保证B取到最后的硬币,B必胜。

AC代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <cstring>
#include <cmath>
#include <set>
#include <iterator>

using namespace std;

typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f3f
//判断是否需要开ll!!!
//判断是否需要初始化!!!
const int mod=1e9+7;
int n; 

int main()
{
//	freopen("test.in","r",stdin);
//  freopen("output.in", "w", stdout);
    while(cin>>n)
    {
    	if(n==0) break;
    	if(n>=3) cout<<"Bob"<<'\n';
    	else cout<<"Alice"<<'\n';
	}
	return 0;
}

os:一开始看错条件了WA了一发

D - Red and Blue(前缀和)

定义函数f(a),有两个数列r,b,将两数列按照原有的顺序合并,求最大的f(a)值。

思路:分别求两数列的前缀和,将最大的两个前缀和相加。

AC代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f3f
//判断是否需要开ll!!!
//判断是否需要初始化!!!
const int mod=1e9+7;
const int N=200;
int t,n,m,r[N],b[N];
int R[N],B[N];

int main()
{
//	freopen("test.in","r",stdin);
//  freopen("output.in", "w", stdout);
    cin>>t;
    while(t--)
    {
    	memset(r,0,sizeof(r));
    	memset(R,0,sizeof(R));
    	memset(b,0,sizeof(b));
		memset(B,0,sizeof(B));
    	cin>>n;
    	int maxn1=0,maxn2=0;
    	for(int i=1;i<=n;i++)
    	{
    		cin>>r[i];
    		R[i]=R[i-1]+r[i];
    		maxn1=max(maxn1,R[i]);
		}
		cin>>m;
    	for(int i=1;i<=m;i++)
    	{
    		cin>>b[i];
    		B[i]=B[i-1]+b[i];
    		maxn2=max(maxn2,B[i]);
		}
		cout<<maxn1+maxn2<<'\n';
	}
	return 0;
}

E - Expanding Rods(几何,二分)

给出升温后管道长度,求拱形高度。

思路: 铁棒弯曲后对应的圆的圆心在中线线上,且每一个R,就可以确定一个弧长,枚举R,求出对应的弧长l,不断逼近L,二分查找即可。部分推导:

AC代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <vector>
#include <map>
#include <queue>
#include <cstring>
#include <cmath>
#include <set>

using namespace std;

typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f3f
//判断是否需要开ll!!!
//判断是否需要初始化!!!
const int mod=1e9+7;
double L,n,C;

int main()
{
//	freopen("test.in","r",stdin);
//  freopen("output.in", "w", stdout);
    while(cin>>L>>n>>C)
    {
    	if(L<0&&n<0&&C<0) break;
    	double ans,l=0.0,r=0.5*L;
    	double L1=(1+n*C)*L;
    	while(r-l>1e-5)
    	{
    		ans=(l+r)/2;
    		double R=(4*ans*ans+L*L)/(8*ans);
    		if(2*R*asin(L/(2*R))<L1) l=ans;
    		else r=ans;
		}
		printf("%.3f\n",ans);
	}
	return 0;
}

os:还能这样做!一开始以为是按照正常数学公式推出来的,结果是二分试值?POJ好奇怪,double只能用/f输出

G - Teleporter(循环)

每一个城镇i都有一个通往a[i]的传送机,从第一个城镇开始,问传送k次被传送至哪个城镇。

思路:很容易看出传送是有循环的,先while循环找出传送循环长度,计算循环的余数即可。

AC代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f3f
//判断是否需要开ll!!!
//判断是否需要初始化!!!
const int mod=1e9+7;
const int N=2e5+5;
int n,a[N],c[N];
ll k;

int main()
{
//	freopen("test.in","r",stdin);
//  freopen("output.in", "w", stdout);
    cin>>n>>k;
    ll v=k;
    for(int i=1;i<=n;i++) cin>>a[i];
    int b=1,cnt=0;
    map<int,int>mp;
    while(k)
    {
    	
    	c[++cnt]=b;
    	if(mp[b]) break;
    	mp[b]++;
    	b=a[b];
    	k--;
	}
	if(k==0) cout<<b<<'\n';
	else
	{
		for(int i=1;i<=cnt;i++)
		{
			if(c[i]==b)
			{
				pos=i;
				break;
			} 
		}
		int dis=cnt-pos;
		int Pos=(v+1-(pos-1))%dis;
		if(Pos==0) Pos=dis;
		cout<<c[pos+Pos-1]<<'\n';
	}
	return 0;
}

os:注意很多细节,计算循环长度边界是否需要加减!

I - Regular Bracket Sequence(思维)

给出一个包含(,),?的字符串,?可以被任意变为其他符号,问是否可以成为一个对称字符串。

思路:若字符串长度为奇数,那必不满足条件;若为偶数,只要开头不是),结尾不是(,都可以满足条件。

AC代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f3f
//判断是否需要开ll!!!
//判断是否需要初始化!!!
const int mod=1e9+7;
int t;
string s;

int main()
{
//	freopen("test.in","r",stdin);
//  freopen("output.in", "w", stdout);
    cin>>t;
    while(t--)
    {
    	cin>>s;
    	int len=s.length();
    	if(len&1) cout<<"NO"<<'\n';
    	else
    	{
    		if(s[0]!=')'&&s[len-1]!='(') cout<<"YES"<<'\n';
    		else cout<<"NO"<<'\n';
		}
	}
	return 0;
}

os:思维题

J - Ceil Divisions(思维)

给出1~n的序列,最多经过n+5次操作,使得数列变为由n-1个1和1个2组成的序列,问怎样操作。

思路: 很容易想到的一种思路是,对(i,i+1)进行操1那么我们就可以找\sqrt{n},由于数据范围是2e5,求根号最多5次就可以了,把所有n求根号的数拿出来单独处理,剩下的按照(i,i+1)处理即可。

AC代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f3f
//判断是否需要开ll!!!
//判断是否需要初始化!!!
const int mod=1e9+7;
int t,n; 

int main()
{
//	freopen("test.in","r",stdin);
//  freopen("output.in", "w", stdout);
    cin>>t;
    while(t--)
    {
        cin>>n;
        int m=n-3;
        int k=n;
        vector<int> a;
        a.push_back(k);
        while(k>=3)
        {
            k=sqrt(k)+1;
            a.push_back(k);
        }
        if(a[a.size()-1]!=2) a.push_back(2);
        cout<<m+a.size()<<endl;
        int i;
        for(k=a.size()-2, i=3; i<n; i++)
        {
            if(a[k]==i) k--;
            else
                cout<<i<<" "<<i+1<<endl;
        }
        for(i=0; i<a.size()-1; i++)
        {
            cout<<a[i]<<" "<<a[i+1]<<endl;
            cout<<a[i]<<" "<<a[i+1]<<endl;
        }
    }
	return 0;
}

K - Building a Fence(判断循环)

 给出长度为k宽为1的木板,给出地面高度,要满足条件构造篱笆,是否可以满足条件?

思路:设l,r为当前篱笆的最低高度和最高高度,每段篱笆的长度都为k,那么下一段的范围是[l-k+1,r+k-1],再加上地面高度a[i]因素,l=max(l-k+1,a[i]),r=min(r+k-1,a[i]+k-1),循环判断是否满足条件即可。

 AC代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f3f
//判断是否需要开ll!!!
//判断是否需要初始化!!!
const int mod=1e9+7;
const int N=2e5+5;
int t,n,k;
int a[N];

int main()
{
//	freopen("test.in","r",stdin);
//  freopen("output.in", "w", stdout);
    cin>>t;
    while(t--)
    {
    	cin>>n>>k;
    	for(int i=1;i<=n;i++) cin>>a[i];
    	bool flag=1;
    	int l=a[1],r=a[1];
    	for(int i=2;i<=n;i++)
    	{
    		l=max(l-k+1,a[i]);
    		r=min(r+k-1,a[i]+k-1);
    		if(l>r)
    		{
    			flag=0;
    			break;
			}
		}
		if(a[n]<l||a[n]>r) flag=0;
		cout<<(flag?"Yes":"No")<<'\n';
	}
	return 0;
}

L - : (Colon)(几何)

给出分针,时针长度,时间,求两针尖距离。

思路:知道时间可以得出两针夹角,余弦定理求对边长度。

AC代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f3f
//判断是否需要开ll!!!
//判断是否需要初始化!!!
const int mod=1e9+7;
int a,b,h,m;


int main()
{
//	freopen("test.in","r",stdin);
//  freopen("output.in", "w", stdout);
    cin>>a>>b>>h>>m;
    double ang=abs(5.5*m-30*h);
    if(ang>180)
    ang=360-ang;
    //cout<<ang<<'\n';
    ang=ang*3.1415926535898/180;
    double q=a*a+b*b-cos(ang)*2*a*b;
    printf("%.12lf\n",sqrt(q));
	return 0;
}

os:计算角度公式可以记住。 

M - Colorful Blocks(组合数学)

n个空格涂色,有m种颜色,最多允许k对两个相邻格子颜色相同,m种颜色不一定都用,求有多少种上色方案。

思路:枚举相邻的位置有i个,可以视作从n-1个(k,k+1)中选出i个出来,方案数为C(n-1,i),那么剩下的n-i个位置一定不相同,方案数为m*((m-1)^(n-i-1))。

AC代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define INF 0x3f3f3f3f
//判断是否需要开ll!!!
//判断是否需要初始化!!!
const int mod=998244353;
const int N=2e6+5;
ll n,m,k;
ll fac[N],inv[N];

ll pmod(ll a,ll b)
{
    ll ans=1%mod;
	a%=mod;
    while(b)
	{
        if(b&1)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}

void init()
{
    fac[0]=1;
    for(int i=1;i<N;i++)
	fac[i]=fac[i-1]*i%mod;
    inv[N-1]=pmod(fac[N-1],mod-2);
    for(int i=N-2;i>=0;i--)
	inv[i]=(i+1)*inv[i+1]%mod;
}

ll C(ll n,ll m)
{
    if(m<0||m>n)return 0;
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}

int main()
{
//	freopen("test.in","r",stdin);
//  freopen("output.in", "w", stdout);
    init();
    cin>>n>>m>>k;
    ll ans=0;
    for(ll i=0;i<=k;i++)
	{
        ll t=C(n-1,i);
        ll tt=m*pmod(m-1,n-i-1)%mod;
        ans=(ans+t*tt%mod)%mod;
    }
    cout<<ans<<endl;
	return 0;
}

若有错误请指教orzorz

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值