2020ICPC南京站总结+补题

本次我们打星参赛,rk95,在银牌区内,希望济南站能发挥出应有的水平拿到银牌。

按开题顺序给上思路与代码。

目录

    • L
    • K
    • F
    • E
    • H
    • M

L

队友1秒签,我也不知道发生了甚么事情。

#include 

using namespace std;

int t,n,m,a[100005],b[100005],pa,pb,c,ma;

int main()
{
    cin>>t;
    while(t--){
        cin>>n>>m;
        for(int i=0;i<n;i++){
            cin>>a[i];
        }
        for(int i=0;i<m;i++){
            cin>>b[i];
        }
        sort(a,a+n);
        sort(b,b+m);
        pa=c=ma=0;
        for(int i=0;i<m;i++){
            c=0;
            while(a[pa]<b[i]&&pa<n){
                c++;
                pa++;
            }
            ma=max(ma,c);
            while(a[pa]<=b[i]&&pa<n){
                pa++;
            }
        }
        ma=max(n-pa,ma);
        if(ma==0){
            cout <<"Impossible" << endl;
        }else{
            cout <<ma << endl;
        }

    }
    return 0;
}

K

队友2跟我一起看的。看着像个很厉害的题,实际上很假。
不难发现对于任意的正整数x,x+1,x与x+1互质,x+1与x互质。
此外,1与任何数的gcd都为1。
这样就得解了,讨论k,当k为奇数时让1保持不变,1后面的k-1个数两两调换位置,如
给出n=5,k=3,5个数为:
1,2,3,4,5
则变为
1,3,2,5,4
当k为偶数时,则直接让前k个数两两调换位置。
代码其实写的挺丑的,好在1A了。

#include 

using namespace std;

int n, k;

int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> k;
    if(k==0)cout << -1 << endl;
    else {
        if(k&1){
            cout << 1;
            for(int i = 2; i < k;  i+=2){
                cout << ' ' <<  i+1 << ' ' << i;
            }
            for(int i = k+1; i <= n; ++i)cout << ' ' << i;
        }
        else {
            for(int i = 1; i <= k;  i+=2){
                if(i != n)cout << i+1 << ' ' << i << ' ';
                else cout << i+1 << ' ' << i;
            }
            for(int i = k+1; i <= n; i++){
                if(i != n)cout << i << ' ';
                else cout << i;
            }
        }
    }

    return 0;
}

F

题意是要求至少一次完美施放所消耗的时间的期望。
我们假设做k个烟火,然后施放一次,那么这一组施放至少出现一次完美释放的概率就是1-(10000-p)^k,假设这个值为t,那么至少一次完美施放所消耗的时间就可以表示为(k*n+m)*1/t。
我们要找到一个整数k,使得上面这个式子的值尽可能小。
不难发现,实际上这个式子与k的关系是先递减后递增,存在一个极小值,找到这个极小值即可。
队友1写了个类似于2分的代码解决了,主要是中间判了下当前位置是递减还是递增。

#include 

using namespace std;
typedef long long ll;

ll t,n,m,p,pl,kr;
double la ;

double getE(ll k){
    return (k*1.0*n+m)/(1-pow((10000.0-p)/10000,k));
}

ll f(ll l,ll r){
    if(l>=r-2){
        return l;
    }
    ll mid=(l+r)/2;
    if(getE(mid)>getE(mid+1)){
        return f(mid,r);
    }else{
        return f(l,mid+1);
    }
}

int main()
{
    cin>>t;
    while(t--){
        cin>>n>>m>>p;
        la=getE(1);
        pl=2;
        while(getE(pl)>=la){
            la=getE(pl);
            la*=2;
        }
        kr=f(1,la);
        printf("%.10lf\n",min(min(getE(kr),getE(kr+1)),getE(kr+2)));
    }

    return 0;
}

E

不难发现只当炸弹在终点,或只能沿一个方向走且炸弹在路径上时才会无解,这些情况可以特判处理一下,然后就是找一条路径规避掉炸弹点。
写起来细节很多,wa了两发后过的。

#include 

using namespace std;

int t,mx,my,tx,ty,u,d,l,r;
string str;

void pc(char c,int n){
    while(n--){
        putchar(c);
    }
}

int main()
{
    cin>>t;
    while(t--){
        cin>>mx>>my>>str;
        u=d=l=r=0;
        for(int i=0;str[i];i++){
            switch(str[i]){
            case 'U':u++;break;
            case 'D':d++;break;
            case 'L':l++;break;
            case 'R':r++;break;
            }
        }
        ty=u-d;tx=r-l;
        if(ty!=my&&0!=mx){
            pc('U',u);pc('D',d);pc('L',l);pc('R',r);
        }else if(tx!=mx&&0!=my){
            pc('L',l);pc('R',r);pc('U',u);pc('D',d);
        }else if((ty==my&&tx==mx)||(0==my&&0==mx)){
            cout<<"Impossible";
        }else if(tx==0&&ty==0){
            if(mx>0){
                pc('L',l);pc('R',r);
            }else{
                pc('R',r);pc('L',l);
            }
            if(my>0){
                pc('D',d);pc('U',u);
            }else{
                pc('U',u);pc('D',d);
            }
        }else if(tx==0){
            if(l!=0){
                pc('L',l);pc('U',u);pc('D',d);pc('R',r);
            }else if(my>ty&&my>0){
                pc('D',d);pc('U',u);
            }else if(my<ty&&my<0){
                pc('U',u);pc('D',d);
            }else{
                cout<<"Impossible";
            }
        }else{
            if(u!=0){
                pc('U',u);pc('L',l);pc('R',r);pc('D',d);
            }else if(mx>tx&&mx>0){
                pc('L',l);pc('R',r);
            }else if(mx<tx&&mx<0){
                pc('R',r);pc('L',l);
            }else{
                cout<<"Impossible";
            }
        }
        cout<<endl;
    }
    return 0;
}

另外给一些构造的样例,也许能帮你找到你的wa点。

1 0
LLRR
-1 0
LLRR
0 1
UUDD
0 -1
UUDD
1 0
RRUD
-1 0
LLUD
0 1
LRUU
0 -1
LRDD
0 1
LRUD

H

队友2以为是个容斥,感觉不可做。最后发现了可能是打表,但错误估计了打表范围,时间也不够了。
突破口在于当矩阵足够大时不存在找不到目标矩形的矩阵。

M

根据榜盲猜贪心,写了两个小时,发现样例过不了后甚至想出了反向加点取min的操作,不出意外的wa了。
实际上是一道比较常规的树形dp,但我们没人会写,挺可惜的。
dp的核心有两个点,一个是枚举时只枚举到子树大小,就是子树中删除了多少个点,然后结合当前顶点删没删来更新当前节点的答案;第二个点是当前顶点删不删减少的贡献并不在当前顶点计算,只是标记一下,用以在枚举子节点时更新状态。
dp从0开始,0是虚拟节点,在这里更新1号节点删与不删增加的贡献。

#include 
typedef long long ll;
using namespace std;
const int maxn = 2010;
ll dp[maxn][maxn][2];
ll hp[maxn];
ll sum[maxn];
ll son[maxn];
vector<int> g[maxn];
ll tmp[maxn][2],t1[maxn][2];
int f[maxn];
void dfs(int u)
{
    son[u] = 1;
    vector<pair<int,int> > vec;
    for(auto v : g[u])
    {
        dfs(v);
        vec.push_back({son[v],v});
    }
    //sort(vec.begin(),vec.end(),greater>() );
    for(auto p : vec)
    {
        int v = p.second;
        memset(tmp,0,sizeof(tmp));
        for(int j = 0;j <= son[v];j++)
        {
            tmp[j][0] = max(tmp[j][0],dp[v][j][0]);
            tmp[j][1] = max(tmp[j][1],dp[v][j][0]);
            tmp[j+1][0] = max(tmp[j+1][0],dp[v][j][1]+sum[v]+((v>1)?hp[v]:0ll));
            tmp[j+1][1] = max(tmp[j+1][1],dp[v][j][1]+sum[v]);

        }
        memset(t1,0,sizeof(t1));
        for(int j = 0;j <= son[v];j++)
        {
            for(int k = 0;k <= son[u];k++){
                t1[j+k][0] = max(t1[j+k][0],tmp[j][0]+dp[u][k][0]);
                t1[j+k][1] = max(t1[j+k][1],tmp[j][1]+dp[u][k][1]);
            }
        }
        son[u]+=son[v];
        for(int j = 0;j <= son[u];j++)
        {
            dp[u][j][0] = t1[j][0];
            dp[u][j][1] = t1[j][1];
        }
    }
}
void solve()
{
    int n;
    ll ans = 0;
    cin >> n;
    for(int i = 0;i <= n;i++) g[i].clear();
    g[0].push_back(1);
    for(int i = 2;i <= n;i++)
    {

        cin >> f[i];
        g[f[i]].push_back(i);
    }
    for(int i = 1;i <= n;i++)
    {
        cin >> hp[i];
        ans+=hp[i];
        if(i>1) ans += hp[i];
        sum[i] = hp[i];
    }
    for(int i = 2;i <= n;i++)
    {
        sum[f[i]]+=sum[i];
    }
    for(int i = 0;i <= n;i++)
    {
        memset(dp[i],0,sizeof(dp[i]));
    }
    dfs(0);
    for(int i = 0;i <= n;i++)
    {
        if(i) cout << ' ';
        cout << ans-max(dp[0][i][0],dp[0][i][1]);
    }
    cout << endl;
    return;
}
int main()
{
    int T;
    ios::sync_with_stdio(false);
    cin >> T;
    while(T--)
    {
        solve();
    }

    return 0;
}

你可能感兴趣的:(算法竞赛相关,学习笔记)