第 45 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(南京)

K:Co-prime Permutation

  签到题。一个常见的套路:相邻两个数的最大公因子是1.这个结论的出来以后,除了 k ≠ 0 k\not =0 k=0的情况时Impossible,其他情况都可以通过将原本下标和值相等的序列右移一位来得到正确答案。

L:Let’s Play Curling

  签到题。题目想求解的是,在给定 c c c的情况下,能够找到多少个 a i a_i ai,使得对于所有的 b i b_i bi,都有 ∣ a i − c ∣ < ∣ b i − c ∣ \big|a_i-c\big |<\big|b_i-c\big| aic<bic.
  使用贪心的策略:两个 b i b_i bi之间的 a i a_i ai的个数就是我们的答案。如果想在更大的区间( b i b_i bi b i + 2 b_{i+2} bi+2)内找到一个 c c c,使得在 b i b_i bi两边的 a i a_i ai都能满足条件,显然是不现实的,这样还是会把大区间分解成两个小区间,我们直接使用上述的贪心策略即可。在具体求解的时候,可以考虑将数组 a a a b b b去重,并对数组 a a a中出现的元素计数(不能直接采用数组的方式)。

#include<bits/stdc++.h>
#define close ios::sync_with_stdio(false)
using namespace std;
const int maxn=1e5+100;
int a[maxn],b[maxn];
map<int,int> mp;
int main()
{
    
    
    close;
    int T;cin>>T;
    while(T--)
    {
    
    
        int n,m;cin>>n>>m;
        mp.clear();
        for(int i=0;i<n;++i) cin>>a[i],mp[a[i]]++;
        for(int j=1;j<=m;++j) cin>>b[j];
        sort(a,a+n);int size=unique(a,a+n)-a;
        sort(b+1,b+m+1);int p=0;int s=unique(b+1,b+m+1)-b-1;
        b[s+1]=1e9+100;b[0]=0;
        int ans=0;
        for(int i=1;i<=s+1 && p<size;++i)
        {
    
    
            int tmp=0;
            while(p<size && a[p]<b[i]) tmp+=mp[a[p]],p++;
            if(tmp>ans) ans=tmp;
            if(a[p]==b[i]) p++;
        }
        if(ans==0) cout<<"Impossible"<<endl;
        else cout<<ans<<endl;
    }
}

E:Evil Coordinate

  赛中把这道题考虑成一道分情况讨论的题目了…如果真的要分类讨论,并且要给出一种可行的构造方案(不通过给出序列然后验证的方式),会需要非常大的思维量,赛中AC代码:https://ac.nowcoder.com/acm/contest/view-submission?submissionId=46179825
  看了题解明白其实还有一种做法,就是暴力枚举,但是我们不会暴力枚举所有的排列组合,而只是枚举U,D,L,R四种方式的排列即可(一走就走完,这种做法肯定没有问题)。

#include<bits/stdc++.h>
#define close ios::sync_with_stdio(false)
using namespace std;
string goal="UDLR";
int num[4];
string Construct(int a,int b,int c,int d)
{
    
    
    string ans="";
    for(int i=0;i<num[a];++i) ans+=goal[a];
    for(int i=0;i<num[b];++i) ans+=goal[b];
    for(int i=0;i<num[c];++i) ans+=goal[c];
    for(int i=0;i<num[d];++i) ans+=goal[d];
    return ans;
}
bool Check(int x,int y,string ans)
{
    
    
    int curx=0,cury=0;
    if(x==0 && y==0) return false;
    int len=ans.length();
    for(int i=0;i<len;++i)
    {
    
    
        if(ans[i]=='U') cury++;
        else if(ans[i]=='D') cury--;
        else if(ans[i]=='L') curx--;
        else curx++;
        if(curx==x && cury==y) return false;
    }
    cout<<ans<<endl;
    return true;
}
bool solve(int x,int y,string s)
{
    
    
    int len=s.length();
    for(int i=0;i<4;++i) num[i]=0; 
    for(int i=0;i<len;++i)
    {
    
    
        if(s[i]=='U') num[0]++;
        else if(s[i]=='D') num[1]++;
        else if(s[i]=='L') num[2]++;
        else num[3]++;
    }
    for(int i=0;i<4;++i)
        for(int j=0;j<4;++j)
            for(int k=0;k<4;++k)
                for(int l=0;l<4;++l)
                {
    
    
                    if(i==j || i==k || i==l || j==k || j==l || k==l) continue;
                    string ans=Construct(i,j,k,l);
                    if(Check(x,y,ans)) return true;
                }
    return false;
}
int main()
{
    
    
    close;
    int T;cin>>T;
    while(T--)
    {
    
    
        int x,y;cin>>x>>y;
        string s;cin>>s;
        if(!solve(x,y,s)) cout<<"Impossible"<<endl;
    }
}

F:Fireworks

  我们令 q = 1 − p / 10000.0 q=1-p/10000.0 q=1p/10000.0,那么成功的概率就是 1 − q x 1-q^x 1qx,那么如果我们在第一次制作 x x x个烟花失败了,我们会回到初始状态,这个时候我们会再次选择做 x x x个烟花(因为我们认为 x x x是这里的最优解,那么我们失败的时候肯定会选择最优解继续做下去),那么如果找到这个最优解,其实就是求解 f ( x ) = x ∗ n + m 1 − q x f(x)=\frac {x*n+m}{1-q^x} f(x)=1qxxn+m,我们想要计算 x x x是整数时的最小值。
f ( x ) = x ∗ n + m 1 − q x f(x)=\frac {x*n+m}{1-q^x} f(x)=1qxxn+m ∴ f ′ ( x ) = n ( 1 − q x ) − ( n x + m ) ( − q x ⋅ ln ⁡ q ) ( 1 − q x ) 2 \therefore f'(x)=\frac {n(1-q^x )-(nx+m)(-q ^x\cdot\ln{q})}{(1-q ^x) ^2} f(x)=(1qx)2n(1qx)(nx+m)(qxlnq) 令 T ( x ) = n ( 1 − q x ) − ( n x + m ) ( − q x ⋅ ln ⁡ q ) , 则 有 : 令T(x)=n(1-q^x )-(nx+m)(-q ^x\cdot\ln{q}),则有: T(x)=n(1qx)(nx+m)(qxlnq) T ′ ( x ) = ( n ⋅ ln ⁡ 2 q ⋅ x + m ⋅ ln ⁡ 2 q ) ⋅ q x > 0 恒 成 立 ( x > 0 ) T'(x)=(n\cdot{\ln ^2 q}\cdot x+m\cdot{\ln ^2 q})\cdot q ^x>0恒成立(x>0) T(x)=(nln2qx+mln2q)qx>0(x>0)因此,我们可以得出 T ( x ) T(x) T(x)单调递增,又由于 lim ⁡ n → 0 T ( x ) < 0 \lim_{n \to 0} T(x)<0 limn0T(x)<0,因此 f ( x ) f(x) f(x)是个单峰函数(先减后增)。
  在明确了 f ( x ) f(x) f(x)是个单峰函数以后,我们就可以使用三分法,注意不同的三分判断条件可能导致答案离正确答案还有些许的距离(误差),要在一个小范围内找到正确的答案!

#include<bits/stdc++.h>
#define close ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;
double n,m,p,q;
const double eps=1e-6;
inline double poww(double base,ll x)
{
    
    
    double ans=1.0;
    while(x){
    
    if(x&1) ans=ans*base;base=base*base;x>>=1;}
    return ans;
}
double cal(ll x) {
    
    return (n*x+m)/(1.0-poww(q,x));}
int main()
{
    
    
    close;int T;cin>>T;
    while(T--)
    {
    
    
        cin>>n>>m>>p;
        q=1-p/10000.0;
        ll l=1,r=1e13+100;
        while(l<r)
        {
    
    
            ll mid=(l+r)/2,midd=(mid+r)/2;
            double ans1=cal(mid),ans2=cal(midd);
            if(ans1-ans2>=eps) l=mid;
            else r=midd;
        }
        double res=LONG_LONG_MAX;
        ll lbound=max(l-5,1ll),rbound=l+5;
        for(int i=lbound;i<=rbound;++i)
            res=min(res,cal(i));
        printf("%.10f\n",res);
    }
}

猜你喜欢

转载自blog.csdn.net/CUMT_William_Liu/article/details/111466560