2021ICPC济南- J Determinant 高斯消元+大数取模,D三分,C递推+组合;

142 篇文章 1 订阅
92 篇文章 0 订阅

1393C - Pinkie Pie Eats Patty-cakes 二分/公式

最小值要最大一看就很像二分,然后一直wa,最后发现check错了,间隔着放是合理的,但是写到check里会出现问题,比如 4 4 4 4 3 3 2 2 1 1,正确的check应该是如果[i-x,i],k这个数没有出现过,就可以选择k放在这个位置,但是k可能有很多,那么一定是次数最多的是最优的,如果对于一个位置不能放数了,那就不合法;

题解 CF1393C 【Pinkie Pie Eats Patty-cakes】 - huayucaiji 的博客小屋 - 洛谷博客 (luogu.com.cn)

还有一个做法是推公式,感觉推公式才是正解,,,

把出现次数最多的数作为挡板来把别的数间隔掉,比如1 1 1 1 2 2 2 2 3 4 5 6 7 8,出现次数最多的就是1和2,那么就可以变为这种形式,1 2 3 4 1 2 5 6 1 2 7 8 1 2,出现次数最多的次数为x,有num个出现次数最多的数,那么公式就是(n-x*num)/(x-1)+num-1,

n-num*x是剩下的数,除以(x-1)就是每段里面可以放几个数,num-1是作为挡板的数的贡献,比如1可以作为2的贡献;

C. Pinkie Pie Eats Patty-cakes(构造,均分)_issue是fw的博客-CSDN博客

粘一下二分的代码

#include<bits/stdc++.h>
#define int long long
#define lowbit(x) ((x)&(-x))
#define endl '\n'
#define pause system("pause")
using namespace std;
const int N=1e6+5;
const int mod=1e9+7;
const int inf=1e18;
int t,n,a[N],tx[N],fk[N],b[N];
struct node
{
    int val,id;
    bool operator<(const node&a)const
    {
        return a.val>val;
    }
};
bool check(int x)
{
    for(int i=1;i<=n;i++) b[i]=0,tx[i]=fk[i];
    priority_queue<node>q;
    for(int i=1;i<=n;i++)
    {
        if(tx[i]) q.push(node{tx[i],i});
    }
    for(int i=1;i<=n;i++)
    {
        if(i>x+1)
        {
            if(tx[b[i-x-1]]) q.push(node{tx[b[i-x-1]],b[i-x-1]});
        }
        if(q.empty()) return 0;
        node u=q.top();q.pop();
        tx[u.id]--;
        b[i]=u.id;
    }
    return 1;
}
signed main()
{
    //ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i=1;i<=n;i++) tx[i]=fk[i]=0;
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];tx[a[i]]++;fk[a[i]]++;
        }
        int l=0,r=n-2,ans;
        while(l<=r)
        {
            int mid=l+r>>1;
            if(check(mid)) ans=mid,l=mid+1;
            else r=mid-1;
        }
        cout<<ans<<endl;
    }
    pause;
    return 0;
}

济南2021ICPC - D Arithmetic Sequence  三分

假设b数组是最后的等差数列,那么答案就是sum(a[i]-b[i]),a[i]-b[i]=a[i]-(i-1)*d-b[1],然后发现答案其实是和d,b[1]有关的,感性的思考一下可以三分d,然后去求b[1],我们发现b[i]=a[i]-(i-1)*d,我们把所有的b[i]求出来以后,固定一个b[i]作为b[1],那么答案就是a[i]-(i-1)*d-B,也就是b[i]-B,B是被选中的数,那么这个问题也就转化成了货舱选址的问题上,选择b中的中位数来作为b[1]是最优的,这样check函数就写完了

2021ICPC济南区域赛题解CDJKL - 知乎 (zhihu.com)

#include<bits/stdc++.h>
#define int long long
#define ll __int128
#define lowbit(x) ((x)&(-x))
#define endl '\n'
#define pause system("pause")
using namespace std;
const int N=1e6+5;
const int mod=1e9+7;
const int inf=1e18;
ll dabs(ll x){return x<0?-x:x;}
ll n,a[N],b[N];
inline ll read(){
    ll x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if(ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
inline void print(ll x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9)
        print(x / 10);
    putchar(x % 10 + '0');
}

ll f(ll d)
{
    for(ll i=1;i<=n;i++) b[i]=a[i]-(i-1)*d;
    nth_element(b+1,b+(n+1)/2,b+n+1);
    //将第(n+1)/2小的数放在第(n+1)/2个位置上
    ll mid=b[(n+1)/2],res=0;
    for(ll i=1;i<=n;i++) res+=dabs(mid-b[i]);
    return res;
}
signed main()
{
    //ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    n=read();
    for(ll i=1;i<=n;i++) a[i]=read();
    ll l=-2e13,r=2e13,ans=0;
    while(l<r)
    {
        ll mid1=l+(r-l)/3;
        ll mid2=r-(r-l)/3;
        //cout<<mid1<<" "<<mid2<<endl;
        if(f(mid1)<f(mid2)) r=mid2-1;
        else l=mid1+1;
    }
    ans=min(f(l),f(r));
    print(ans);
    pause;
    return 0;
}

2021ICPC济南- J Determinant 高斯消元+大数取模

数据范围很大,不能直接高斯消元后去统计负号的个数,需要对大数取模;

定理:对于一个数x和一个奇数p,x%p!=(-x)%p

然后再用高斯消元做就可以了,注意行列式的性质:交换两行,行列式变号,还要注意取模,,

【2021ACM-ICPC亚洲区域赛济南站】C、D、J、K四题超详细题解_keguaiguai的博客-CSDN博客_icpc济南

#include<bits/stdc++.h>
#define int long long
#define ll __int128
#define lowbit(x) ((x)&(-x))
#define endl '\n'
#define pause system("pause")
using namespace std;
const int N=1e6+5;
const int mod=1e9+7;
const int inf=1e18;
int qpow(int a,int b)
{
    int res=1;
    while(b)
    {
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
int getinv(int a){return qpow(a,mod-2);}
int t,n,a[105][105],sum=0;
char s[N];
void gauss()
{
    int nl=1,fu=0,res=1;
    for(int k=1;k<=n;k++)
    {
        int p=nl;
        for(int i=nl+1;i<=n;i++)
        if(a[p][k]<a[i][k]) p=i;
        if(a[p][k]==0) continue;
        if(p!=nl) res=-res,swap(a[p],a[nl]);
        int inv=getinv(a[nl][k]);
        res=(res*a[nl][k]%mod+mod)%mod;
        for(int i=nl+1;i<=n;i++)
        {
            int tmp=a[i][k]*inv%mod;
            for(int j=k;j<=n;j++)
           //a[i][j]=((a[i][j]-tmp*a[nl][j]%mod)%mod+mod)%mod;
            a[i][j]=(a[i][j]-tmp*a[nl][j]%mod+mod)%mod;
        }
        nl++;
    }
    if(res==sum) cout<<"+\n";
    else cout<<"-\n";
}
signed main()
{
   // ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--)
    {
        cin>>n;
        cin>>(s+1);sum=0;
        for(int i=1;i<=strlen(s+1);i++)
        {
            int x=s[i]-'0';
            sum=(sum*10%mod+x)%mod;
        }
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) cin>>a[i][j];
        gauss();
    }
    pause;
    return 0;
}

2021济南ICPC - C Optimal Strategy 递推+组合

谁赢谁输一开始就知道,假设最后A拿了X分,B拿了Y分,那么只要是最后两人拿的序列的和是X和Y就可以,考虑对一个数i来说,i出现的次数是tx[i],如果tx[i]为偶数,那么先手一定会先拿走一个把偶数的局面留给后手;如果是偶数,那么两个人拿的数量是一样的;这样其实就可以一对一对的考虑,假设f[i-1]是拿了小于i的方案数,那么对于i这个数来说,如果是偶数,那每对都可以插到任意位置,如果是奇数,那么先手一定先把i拿走,然后局面又和偶数一样了,所以f[i]=f[i-1]*C(sum+x,x)*fac[tx[i]],sum是小于i的总个数,x=tx[i]/2,fac[tx[i]]是阶乘,因为顺序可以不同,因为是按空插进去,一共有sum+x个空,插入x对,所以是C(sum+x,x)

【2021ACM-ICPC亚洲区域赛济南站】C、D、J、K四题超详细题解_keguaiguai的博客-CSDN博客_icpc济南

#include<bits/stdc++.h>
#define int long long
#define ll __int128
#define lowbit(x) ((x)&(-x))
#define endl '\n'
#define pause system("pause")
using namespace std;
const int N=1e6+5;
const int mod=998244353;
const int inf=1e18;
int qpow(int a,int b) 
{
    int res=1;
    while(b)
    {
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
int getinv(int a){return qpow(a,mod-2);}
int fac[N],ifac[N],f[N],n,a[N],tx[N];
int C(int a,int b)
{
    return (fac[a]*ifac[a-b]%mod)*ifac[b]%mod;
}
void init()
{
    fac[0]=1;
    for(int i=1;i<=1e6;i++) fac[i]=fac[i-1]*i%mod;
    ifac[1000000]=getinv(fac[1000000]);
    for(int i=1000000-1;i>=0;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
}
signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    init();
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i],tx[a[i]]++;
    int sum=0;f[0]=1;
    for(int i=1;i<=n;i++)
    {
        int x=tx[i]/2;
        f[i]=(f[i-1]*C(sum+x,x)%mod)*fac[tx[i]]%mod;
        //cout<<"f[i]="<<f[i]<<" sum="<<sum<<" C="<<C(sum+x,x)<<" fac[x]="<<fac[x]<<" t[x]="<<tx[x]<<endl;
        sum+=tx[i];
    }
    cout<<f[n]<<endl;
    pause;
    return 0;
}

2020CCPC绵阳 博弈搜索

当c0=0&&c1=0或者c0=0&&c1<2&&c2=0时先手输,别的直接进行转移,然后发现一个很重要的事情就是如果牌很多的情况下,无论先手如何选,后手总可以到达c0-2或者c1-3的局面,那么一开始搜的时候直接进行取余即可

2020 CCPC绵阳站G.Game of Cards(博弈搜索)_Azir丶的博客-CSDN博客

#include<bits/stdc++.h>
#define int long long
#define ll __int128
#define lowbit(x) ((x)&(-x))
#define endl '\n'
#define pause system("pause")
using namespace std;
const int N=1e6+5;
const int mod=998244353;
const int inf=1e18;
int qpow(int a,int b) 
{
    int res=1;
    while(b)
    {
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
int getinv(int a){return qpow(a,mod-2);}
int t;
bool dfs(int a,int b,int c)
{
    if(a==0&&b==0) return 0;
    if(a==0&&c==0&&b<2) return 0;
    if(a>0&&!dfs(a-1,b,c)) return 1;
    if(b>0&&c>0&&!dfs(a,b-1,c-1)) return 1;
    if(b>1&&!dfs(a,b-2,c+1)) return 1;
    return 0;
}
signed main()
{
    //ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>t;int ca=0;
    while(t--)
    {
        int a,b,c,d;
        cin>>a>>b>>c>>d;
        int flag=0;
        if(!b&&!c&&!d)
        {
            if(!a) flag=0;
            else if(a&1) flag=0;
            else flag=1;
        }
        else flag=dfs(a%2,b%3,c);
        string ans;
        if(flag) ans="Rabbit";
        else ans="Horse";
        cout<<"Case #"<<++ca<<": "<<ans<<endl;
    }
    pause;
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

killer_queen4804

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值