领域极限(三分),ICPC(2019南京三个铜牌题)

刷省选题(2/100)
P6163 [Cnoi2020]领域极限(三分)
昨天用了一个很难的贪心证明去写了,虽然时间复杂度和空间复杂度都是最优的,但是证明非常的难,也很难用那种神奇的角度去思考,所以今天用三分来写一下

首先我们可以看出题目中给出的就是一个公式,也可以说这是一个函数,那么我们选择的数上届显然就是所有区间里r的最大值,下界就是所有区间里l的最小值,那么在这个范围内,函数的值其实呈现出了凹函数的特性,所以我们可以利用三分法来求的极小值。

#include 
using namespace std;
#define int int
const int N=1e5+5;

int n;
int x[N],y[N],a[N];
int pre[N],suf[N];

int calc(int t)
{
    int res=0;
    for(int i=1;i<=n;++i)
    {
        if(y[i]<t) a[i]=y[i];
        else if(x[i]>t) a[i]=x[i];
        else a[i]=t;
    }
    sort(a+1,a+n+1);
    for(int i=1;i<=n;++i) pre[i]=pre[i-1]+a[i];
    for(int i=n;i>=1;--i) suf[i]=suf[i+1]+a[i];
    for(int i=1;i<=n;++i) res+=a[i]*i-pre[i]+suf[i]-a[i]*(n-i+1);
    return res;
}
int main()
{
    int n;
    cin>>n;
    int l=1e9,r=0;
    for(int i=1;i<=n;++i)
    {
        cin>>x[i]>>y[i];
        l=min(l,x[i]),r=max(r,y[i]);
    }
    while(l<=r)
    {
        int lmid=l+(r-l)/3,rmid=r-(r-l)/3;
        if(calc(lmid)<calc(rmid)) r=rmid-1;
        else l=lmid+1;
    }
    cout<<calc(r)<<endl;
    return 0;
}

The 2019 ICPC Asia Nanjing Regional Contest

A题
题意:给一个长度为n个排列,要求你确定一个大小k,使得从这个排列中挑出任意一个大小为k的子集都存在一个二元组(u,v)
使得u是v的因子

思路:我们知道相邻的两个数互质,根据这个规则,我们如果拿出一个序列想要让里面的所有数互质,那最多只能拿走前一半或者后一半,所以只要多拿走一个让他不互质就行了。

#include 

using namespace std;
const int N = 1e6+100;
#define endl '\n'
int a[N];

signed main()
{
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    int t;
    for(cin>>t;t;t--)
    {
        int n;
        cin>>n;
        if(n%2)
        {
            cout<<n/2+2<<endl;
        }
        else
        {
            cout<<n/2+1<<endl;
        }
    }
    return 0;
}

C(DP+记忆化搜索)

题意:给定一个二维数组,我们要求出数组中“路径”的个数,路径的定义是:一些由相邻格子组成,格子内元素有序递增且不可再拓展长度而且长度最少为4的连续格子

思路:我们设dp状态为dp[i][j][k]代表,第(i,j)这个格子作为倒数第k个路径中的格子,这样的路径条数有多少。
至于为什么用倒数第k个,主要是因为避免数组过大,因为题目中说长度至少为4,那么k的取值最大就是4,dp状态也是很好更新的,我们要去下一个格子的时候先看看下个格子是否被搜过,是的话先更新,否则先搜索再更新。

和平时的记忆化搜索不太一样的是内部还含有递推的过程

#include 
#define int long long
#define ios cin.tie(0),cout.tie(0),ios::sync_with_stdio(0);
#define endl '\n'

using namespace std;
const int N = 1e3+100;
const int mod = 1e9+7;
int vis[1005][1005],dp[1005][1005][5],a[1005][1005],n,m;
int dx[4]={0,0,-1,1},dy[4]={1,-1,0,0};
bool check(int x,int y)
{
    for(int i=0;i<4;i++)
    {
        int tx=x+dx[i],ty=y+dy[i];
        if(tx<1||tx>n||ty<1||ty>m) continue;
        if(a[tx][ty]+1==a[x][y]) return 0;
    }
    return 1;
}
void dfs(int x1,int x2,int x3,int x4)
{
    vis[x1][x2]=1;
    int flag=0;
    for(int i=0;i<4;i++)
    {
        int tx=x1+dx[i],ty=x2+dy[i];
        if(tx<1||tx>n||ty<1||ty>m) continue;
        if(a[tx][ty]-1==a[x1][x2])
        {
            flag=1;
            if(vis[tx][ty])
            {
                dp[x1][x2][2]=(dp[x1][x2][2]+dp[tx][ty][1])%mod;
                dp[x1][x2][3]=(dp[x1][x2][3]+dp[tx][ty][2])%mod;
                dp[x1][x2][4]=((dp[tx][ty][4]+dp[tx][ty][3])%mod+dp[x1][x2][4]%mod)%mod;
                continue;
            }
            dfs(tx,ty,x1,x2);
            dp[x1][x2][2]=(dp[x1][x2][2]+dp[tx][ty][1])%mod;
            dp[x1][x2][3]=(dp[x1][x2][3]+dp[tx][ty][2])%mod;
            dp[x1][x2][4]=((dp[tx][ty][4]+dp[tx][ty][3])%mod+dp[x1][x2][4]%mod)%mod;
        }
    }

    if(!flag) dp[x1][x2][1]=1;
    return;
}
signed main()
{
    ios;
    memset(dp,0,sizeof(dp));
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) cin>>a[i][j];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            if(!vis[i][j]) dfs(i,j,0,0);
        }
    int ans=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        if(check(i,j)) {
            ans=(ans+dp[i][j][4])%mod;
        }
    cout<<ans<<endl;
    return 0;
}

G(思维,博弈)

题意:有a个说真话的,b个说假话的,c个有可能说真话也有可能说假话的。确保a至少有一个人。

你需要问这些人的问题是,公主是谁(公主是a个人中的一个)

假设提前知道a,b,c的数量,请求出最小几次才能知道公主是谁。
整个题应该是不难的,思路很好想,但是有个特判,就是当只有一个a的时候,其他都没有,我们一次都不需要询问,因为我们提前知道有多少人同意,所以直接就能猜出公主就是那个唯一的a

#include 

using namespace std;
const int N = 1e6+100;
#define endl '\n'
int a[N];

signed main()
{
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    int a,b,c;
    cin>>a>>b>>c;
    if(a==1&&b==0&&c==0)
    {
        cout<<"YES"<<endl;
        cout<<0<<endl;return 0;
    }
    if(a>b+c)
    {
        cout<<"YES"<<endl;
        cout<<2*(b+c)+1<<endl;
    }
    else
    {
        cout<<"NO"<<endl;
    }
    return 0;
}

你可能感兴趣的:(算法刷题,算法)