GYM 2019 USP-ICMC A、B、C、D、E、F、H、J

题目链接https://codeforces.com/gym/102302

A 单调栈水题

#include
using namespace std;
const int N=1e5+10;
int h[N],n,r[N];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%d",&h[i]);
    stackst;
    st.push(n+1);
    h[n+1]=0x3f3f3f3f;
    for(int i=n;i>=1;--i)
    {
        //printf("i:%d\n",i);
        while(!st.empty()&&h[i]>=h[st.top()]) st.pop();
        r[i]=st.top()-1;
        st.push(i);
    }
    //for(int i=1;i<=n;++i) printf("%d ",r[i]);
    //puts("");
    for(int i=1;i<=n;++i)
    {
        int ans=min(r[i]-i,h[i]);
        printf("%d ",ans);
    }
}

B 分解因子水题

#include
using namespace std;
typedef long long ll;
priority_queue ,greater > ans;
int main()
{
	ll a,b;
	cin>>a>>b;
	for(ll i=1;i*i<=a;++i){
		if(a%i!=0) continue;
		ll t1=a/i,t2=i;
		if(t1%b==0) ans.push(t1);
		if(t2%b==0&&t1!=t2) ans.push(t2);
	}
	while(!ans.empty()){
		printf("%lld ",ans.top());
		ans.pop();
	}
	puts("");
}

C 枚举左下角的端点和右上角的端点,然后二维前缀和判断区间内的点是否等于4个点即可

#include
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=2e3+10,M=2e3;
 
int lx,ly,n;
struct node
{
    int x,y;
}a[N];
int X[N],Y[N];
bool vis[N][N];
int ma[N][N],sum[N][N];
bool cmp(node a,node b)
{
    if(a.x==b.x) a.y>n;
	for(int i=1;i<=n;++i)
    {
        scanf("%d%d",&a[i].x,&a[i].y);
        X[++lx]=a[i].x;
        Y[++ly]=a[i].y;
    }
    sort(X+1,X+1+lx);
    sort(Y+1,Y+1+ly);
    lx=unique(X+1,X+1+lx)-X-1;
    ly=unique(Y+1,Y+1+ly)-Y-1;
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;++i)
    {
        a[i].x=getidx(a[i].x);
        a[i].y=getidy(a[i].y);
        ma[a[i].x][a[i].y]=1;
    }
    for(int i=1;i<=n;++i)
    for(int j=1;j<=n;++j)
    sum[i][j]=ma[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
    int ans=0;
 
    /*for(int i=1;i<=n;++i)
    {
        printf("x:%d y:%d\n",a[i].x,a[i].y);
    }*/
    for(int i=1;i<=n;++i)
    {
        for(int j=i+1;j<=n;++j)
        {
            if(i==j) continue;
            if(a[i].x==a[j].x||a[i].y==a[j].y) continue;
            if(a[i].xa[j].y) continue;
            if(ma[a[j].x][a[i].y]&&ma[a[i].x][a[j].y])
            {
                //if(check(i,j))
                {
                    int x1=min(a[i].x,a[j].x);
                    int y1=min(a[i].y,a[j].y);
                    int x2=max(a[i].x,a[j].x);
                    int y2=max(a[i].y,a[j].y);
                    if(sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1]==4)
                    {
                        //printf("i:%d j:%d\n",i,j);
                        //printf("x:%d y:%d x:%d y:%d\n",x1,y1,x2,y2);
                        vis[a[i].x][a[i].y]=1;
                        vis[a[j].x][a[i].y]=1;
                        vis[a[i].x][a[j].y]=1;
                        vis[a[j].x][a[j].y]=1;
                        ans++;
                    }
                }
            }
        }
    }
    printf("%d\n",ans);
}

D 就是一个找子序列的水题

#include
using namespace std;
int main()
{
	string s,t;
	cin>>s>>t;
	int now = 0;
	for(int i=0;i

F 很思维的概率题,不过化简后的公式很容易被猜出来

求桌子上牌数量的期望,抽到1的时候才会停止。那么就是枚举桌子上数量的num:1,2,3...n

num=1的时候概率期望是1*1/n,因为你要第一次抽到1

num=2 第二次抽到1,第一抽到别的。有种可能就是第二次一直抽到第一次抽到的牌。那可能就是死循环的概率。

不急,这个地方,经历上次多校唯一一场爆零的那场得到经验,这个地方能直接算出期望出来,要么抽第一次的牌,要么抽到1.概率是1/2,那么抽到1的期望就是1/1/2=2;答案就是2又由于1在所有牌抽中的概率是1/n。所以num=2的期望还要乘上这个1/n

num=3 类似。。。

公式化简后就是(1+2+...n)/n

#include
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=1e5+10;
int main()
{
	int n;
	scanf("%d",&n);
	double ans=1.0/n;
	int d=2;
	double t=1.0/n;
 
	//printf("%.10f\n",ans);
	for(int i=2;i<=n;++i)
    {
        ans+=t*i;
        //printf("%.10f\n",ans);
    }
    printf("%.10f\n",ans);
}

E、dp

每个节点选取四个出来进行dp,选最小的两个和最大的两个,不满4的给-1,特判一下。

dp[i][j]代表第i个节点以j为开头位置的最大值,那么转移方程就是:

val就是保存每个节点的值,只需要四个。

dp[i][j]=max(dp[i][j],dp[i-1][p]+abs(val[i][j]-val[i-1][k]));

#include
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=1e6+10;
vectorG[N];
struct node
{
    int id,v;
}a[N];
bool cmp(node a,node b)
{
    return a.id

H、矩阵快速幂优化dp方程

题目要求这么一个序列:a_{i-1}*a_{i+1}<=a_{i}^{2},且ai序列只能包括0、1、2.

设dp方程dp[i][j][k]为第i位时,后面两位分别是j、k的方案数。

j、k的取值方案共九种:00 01 02 10 11 12 20 21 22

那么通过简单观察可以得到递推方程:不好写。不过可以直接构造矩阵快速幂的方程

\begin{pmatrix} \\ 00 \\ 01 \\ 02 \\ 10 \\ 11 \\ 12 \\ 20 \\ 21 \\22 \end{pmatrix}=\begin{pmatrix} 1\0\0\1\0\0\1\0\0\\ 1\0\0\0\0\0\0\0\0 \\ 1\0\0\0\0\0\0\0\0 \\ 0\1\0\0\1\0\0\1\0\\ 0\1\0\0\1\0\0\0\0 \\ 0\1\0\0\0\0\0\0\0\\ 0\0\1\0\0\1\0\0\1\\ 0\0\1\0\0\1\0\0\1 \\ 0\0\1\0\0\1\0\0\1 \end{pmatrix}\begin{pmatrix} \\ 00 \\ 01 \\ 02 \\ 10 \\ 11 \\ 12 \\ 20 \\ 21 \\22 \end{pmatrix}

代码:

#include
using namespace std;
typedef long long ll;
const int MAXN=10;
const ll mod=1000000007 ;

struct Matrix
{
	ll mat[MAXN][MAXN];
	Matrix() {}
	Matrix operator*(Matrix const &b)const
	{
		Matrix res;
		memset(res.mat, 0, sizeof(res.mat));
		for (int i = 0 ;i < MAXN; i++)
		for (int j = 0; j < MAXN; j++)
		for (int k = 0; k < MAXN; k++)
			res.mat[i][j] = (res.mat[i][j]+this->mat[i][k] * b.mat[k][j])%mod;
		return res;
	}
};
Matrix pow_mod(Matrix base, ll n)
{
	Matrix res;
	memset(res.mat, 0, sizeof(res.mat));
	for (int i = 0; i < MAXN; i++)
		res.mat[i][i] = 1;
	while (n > 0)
	{
		if (n & 1) res = res*base;
		base = base*base;
		n >>= 1;
	}
	return res;
}
Matrix base,fi;
int f[9][9]={
1,0,0,1,0,0,1,0,0,
1,0,0,0,0,0,0,0,0,
1,0,0,0,0,0,0,0,0,
0,1,0,0,1,0,0,1,0,
0,1,0,0,1,0,0,0,0,
0,1,0,0,0,0,0,0,0,
0,0,1,0,0,1,0,0,1,
0,0,1,0,0,1,0,0,1,
0,0,1,0,0,1,0,0,1,
};
int main()
{
    ll t=0;
    for(int i=0;i<9;++i)
    {
        for(int j=0;j<9;++j) base.mat[i][j]=f[i][j];//,t+=f[i][j];
    }
    //printf("%lld\n",t);
    ll n;
	scanf("%lld", &n);
    Matrix ans = pow_mod(base, n-2);
    ll res=0;
    for(int i=0;i<9;++i)
    for(int j=0;j<9;++j) res=(res+ans.mat[i][j])%mod;
    printf("%lld\n",res);
	return 0;
}

J  要么枚举中间两个数的,枚举其周围的最近素数即可

include
using namespace std;
typedef long long ll;
const int N = 1e5+100;
ll a[N];
int n;
int check(int x){
	if(x==1) return 0;
	for(int i=2;i*i<=x;i++)
		if(x%i==0) return 0;
	return 1; 
}
ll gao(int x){
	ll ans=0;
	for(int i=1;i<=n;i++) ans+=abs(x-a[i]);
	return ans;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
	sort(a+1,a+1+n);
	ll ans=1e18;
	if(n%2==0){
		for(int i=a[n/2];i<=a[n/2+1];i++){
			if(check(i)){
				ans=min(ans,gao(i));
				break;
			}
		}
		for(int i=a[n/2];i>=2;i--){
			if(check(i)){
				ans=min(ans,gao(i));
				break;
			}
		}
		for(int i=a[n/2+1];i;i++){
			if(check(i)){
				ans=min(ans,gao(i));
				break;
			}
		}
	}else {
		for(int i=a[n/2+1];i>=2;i--){
			if(check(i)){
				ans=min(ans,gao(i));
				break;
			}
		}
		for(int i=a[n/2+1];i;i++){
			if(check(i)){
				ans=min(ans,gao(i));
				break;
			}
		}
	}
	printf("%lld\n",ans);
}

 

你可能感兴趣的:(codeforce题解)