牛客網暑期ACM多校訓練營(第六場)

A
簽到。

#include<bits/stdc++.h>
using namespace std;
const int N=(1<<15)+1;
int t,n;
bool vis[N][15],lose[N];
int num[N][15];
int main(){
    scanf("%d",&t);
    for(int tt=1; tt<=t; tt++){
        scanf("%d",&n);
        for(int i=1; i<=(1<<n); i++){
            for(int j=1; j<=n; j++){
                scanf("%d",&num[i][j]);
            }
            sort(num[i]+1,num[i]+n+1);
        }
        memset(vis,0,sizeof(vis));
        memset(lose,0,sizeof(lose));
        for(int i=1; i<=n; i++){
            int a=-1,b=-1;
            for(int j=1; j<=(1<<n); j++){
                if(lose[j]) continue;
                if(a==-1) a=j;
                else if(b==-1){
                    b=j;
                    int maxai,maxbi;
                    for(int k=n; k>=1; k--){
                        if(vis[a][k]) continue;
                        else{
                            maxai=k;
                            break;
                        }
                    }
                    for(int k=n; k>=1; k--){
                        if(vis[b][k]) continue;
                        else{
                            maxbi=k;
                            break;
                        }
                    }
                    for(int k=maxai-1; k>=1; k--){
                        if(vis[a][k]) continue;
                        if(num[a][k]>num[b][maxbi])
                            maxai=k;
                    }
                    for(int k=maxbi-1; k>=1; k--){
                        if(vis[b][k]) continue;
                        if(num[b][k]>num[a][maxai])
                            maxbi=k;
                    }
                    if(num[a][maxai]>num[b][maxbi])
                        lose[b]=1;
                    else lose[a]=1;
                    vis[a][maxai]=1;
                    vis[b][maxbi]=1;
                    a=-1;
                    b=-1;
                }
            }
        }
        int ans;
        for(int i=1; i<=(1<<n); i++)
            if(lose[i]==0)
                ans=i;
        printf("Case #%d: %d\n",tt,ans);
    }
    return 0;
}

C
推推式子組合數一下。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=998244353;
int t;
ll n,m;

ll qpow(ll x, ll y){
    ll ans=1;
    while(y){
        if(y&1) ans=ans*x%mod;
        y>>=1;
        x=x*x%mod;
    }
    return ans;
}

ll getinv(ll x){
    return qpow(x,mod-2);
}

int inv[1000005];

void init(){
    for(ll i=1; i<=1000000; i++)
        inv[i]=getinv(i);
    return;
}

int main(){
    init();
    scanf("%d",&t);
    for(int tt=1; tt<=t; tt++){
        scanf("%lld%lld",&n,&m);
        ll tmp1=1,tmp2=m%mod,ans=0;
        for(int i=0; i<min(n,m); i++){
            if(i){
                tmp1=tmp1*((n-i)%mod)%mod*inv[i]%mod;
                tmp2=tmp2*((m-i)%mod)%mod;
            }
            //cout<<tmp1<<" "<<tmp2<<endl;
            ans=(ans+tmp1*tmp2)%mod;
        }
        printf("Case #%d: %lld\n",tt,ans);
    }
    return 0;
}

D
簽到。

#include <bits/stdc++.h>
using namespace std;
int n,m,k;
int mx[100003];
int main(){
    int T;
    scanf("%d",&T);
    for(int tt=1;tt<=T;tt++){
        memset(mx,0,sizeof(mx));
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=k;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            mx[v]=max(mx[v],w);
        }
        long long ans=0;
        for(int i=1;i<=m;i++)ans+=mx[i];
        printf("Case #%d: %lld\n",tt,ans);
    }
}

F
樹dp,維護最多的0和1,高精度。

#include<bits/stdc++.h>
using namespace std;
#define bign num
const int N=5005;

const int power = 4;      //每次運算的位數爲10的power次方,在這裏定義爲了方便程序實現
const int base = 10000;      //10的power次方。


//要壓位的時候,只需改power 和 base即可,如壓萬位高精,那麼power = 4, base = 10000


const int MAXL = 200;    //數組的長度。


char a[MAXL], b[MAXL];
struct num
{
    int a[MAXL];
    num() { memset(a, 0, sizeof(a)); }                      //初始化
    num(char *s)                                            //將一個字符串初始化爲高精度數
    {
        memset(a, 0, sizeof(a));
        int len = strlen(s);
        a[0] = (len+power-1) / power;                       //數的長度
        for (int i=0, t=0, w; i < len ;w *= 10, ++i)
        {
            if (i % power == 0) { w = 1, ++t; }
            a[t] += w * (s[i]-'0');
        }
        //初始化數組,這裏自己模擬一下,應該很容易懂的~
    }
    void add(int k) { if (k || a[0]) a[ ++a[0] ] = k; }     //在末尾添加一個數,除法的時候要用到
    void re() { reverse(a+1, a+a[0]+1); }                   //把數反過來,除法的時候要用到
    void print()                                            //打印此高精度數
    {
        printf("%d", a[ a[0] ]);
        //先打印最高位,爲了壓位 或者 該高精度數爲0 考慮
        for (int i = a[0]-1;i > 0;--i)
            printf("%0*d", power, a[i]);
        //這裏"%0*d", power的意思是,必須輸出power位,不夠則前面用0補足
        printf("\n");
    }
} p,q,ans;


bool operator < (const num &p, const num &q)              //判斷小於關係,除法的時候有用
{
    if (p.a[0] < q.a[0]) return true;
    if (p.a[0] > q.a[0]) return false;
    for (int i = p.a[0];i > 0;--i)
    {
        if (p.a[i] != q.a[i]) return p.a[i] < q.a[i];
    }
    return false;
}


num operator + (const num &p, const num &q)               //加法,不用多說了吧,模擬一遍,很容易懂
{
    num c;
    c.a[0] = max(p.a[0], q.a[0]);
    for (int i = 1;i <= c.a[0];++i)
    {
        c.a[i] += p.a[i] + q.a[i];
        c.a[i+1] += c.a[i] / base;
        c.a[i] %= base;
    }
    if (c.a[ c.a[0]+1 ]) ++c.a[0];
    return c;
}


num operator - (const num &p, const num &q)               //減法,也不用多說,模擬一遍,很容易懂
{
    num c = p;
    for (int i = 1;i <= c.a[0];++i)
    {
        c.a[i] -= q.a[i];
        if (c.a[i] < 0) { c.a[i] += base; --c.a[i+1]; }
    }
    while (c.a[0] > 0 && !c.a[ c.a[0] ]) --c.a[0];
    //我的習慣是如果該數爲0,那麼他的長度也是0,方便比較大小和在末尾添加數時的判斷。
    return c;
}


num operator * (const num &p, const num &q)
    //乘法,還是模擬一遍。。其實高精度就是模擬人工四則運算!
{
    num c;
    c.a[0] = p.a[0]+q.a[0]-1;
    for (int i = 1;i <= p.a[0];++i)
        for (int j = 1;j <= q.a[0];++j)
        {
            c.a[i+j-1] += p.a[i]*q.a[j];
            c.a[i+j] += c.a[i+j-1] / base;
            c.a[i+j-1] %= base;
        }
    if (c.a[ c.a[0]+1 ]) ++c.a[0];
    return c;
}


num operator / (const num &p, const num &q)               //除法,這裏我稍微講解一下
{
    num x, y;
    for (int i = p.a[0];i >= 1;--i)                       //從最高位開始取數
    {
        y.add(p.a[i]);             //把數添到末尾(最低位),這時候是高位在前,低位在後
        y.re();                    //把數反過來,變爲統一的存儲方式:低位在前,高位在後
        while ( !(y < q) )         //大於等於除數的時候,如果小於的話,其實答案上的該位就是初始的“0”
            y = y - q, ++x.a[i];   //看能減幾個除數,減幾次,答案上該位就加幾次。
        y.re();                    //將數反過來,爲下一次添數做準備
    }
    x.a[0] = p.a[0];
    while (x.a[0] > 0 && !x.a[x.a[0]]) --x.a[0];
    return x;
}

bign zero=num("0");
bign one=num("1");
bign two=num("2");
int t,n,fa[N],l[N],r[N],c;
char s[20];
bool cap[N][16];
bign dp[N][2];
bign tot[N];

void dfs(int x){
    if(l[x]==0) return;
    dfs(l[x]);
    dfs(r[x]);
    tot[x]=tot[l[x]]*tot[r[x]];
    for(int i=0; i<16; i++){
        if(!cap[x][i]) continue;
        bign tmp[2];
        for(int wl=0; wl<=1; wl++){
            for(int wr=0; wr<=1; wr++){
                tmp[0]=tmp[1]=zero;
                for(int li=0; li<=1; li++){
                    for(int ri=0; ri<=1; ri++){
                        int ans;
                        if(i&(1<<(li*2+ri)))
                            ans=1;
                        else ans=0;
                        bign tmpl,tmpr;
                        if(li==wl)
                            tmpl=dp[l[x]][wl];
                        else tmpl=tot[l[x]]-dp[l[x]][wl];
                        if(ri==wr)
                            tmpr=dp[r[x]][wr];
                        else tmpr=tot[r[x]]-dp[r[x]][wr];

                        tmp[ans]=tmp[ans]+tmpl*tmpr;
                    }
                }
                //cout<<x<<" "<<i<<" "<<wl<<" "<<wr<<" "<<tmp[0]<<" "<<tmp[1]<<endl;
                //dp[x][0]=max(dp[x][0],tmp[0]);
                //dp[x][1]=max(dp[x][1],tmp[1]);
                if(dp[x][0]<tmp[0])
                    dp[x][0]=tmp[0];
                if(dp[x][1]<tmp[1])
                    dp[x][1]=tmp[1];
            }
        }
    }
    //cout<<x<<": "<<dp[x][0]<<" "<<dp[x][1]<<endl;
    return;
}

int main(){
    scanf("%d",&t);
    for(int tt=1; tt<=t; tt++){
        scanf("%d",&n);
        memset(l,0,sizeof(l));
        memset(r,0,sizeof(r));
        for(int i=1; i<=n-1; i++){
            scanf("%s",s);
            for(int j=0; j<16; j++){
                cap[i][j]=s[j]-'0';
            }
        }
        for(int i=2; i<=2*n-1; i++){
            scanf("%d",&c);
            fa[i]=c;
            if(l[c]==0)
                l[c]=i;
            else{
                r[c]=i;
                if(l[c]>r[c])
                    swap(l[c],r[c]);
            }
        }
        /*
           for(int i=1; i<=n-1; i++){
           cout<<i<<":"<<endl;
           cout<<"l:"<<l[i]<<endl;
           cout<<"r:"<<r[i]<<endl;
           }
           */
        for(int i=0; i<=n-1; i++){
            dp[i][0]=zero;
            dp[i][1]=zero;
            tot[i]=zero;
        }
        for(int i=n; i<=2*n-1; i++){
            dp[i][0]=dp[i][1]=one;
            tot[i]=two;
        }
        dfs(1);
        printf("Case #%d: ",tt);
        dp[1][1].print();
        //printf("\n");
    }
    return 0;
}

G
通過觀察猜測到一個結論,s和t的最小割一定是一邊是一個點的情況。樹dp算出每個點到其餘所有點的距離和。最小的取n-1次,次小的取n-2次,以此類推。注意到答案會爆ll,用int128。

#include <bits/stdc++.h>
using namespace std;
inline void read(__int128 &x){
    char ch;
    bool flag=false;
    for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
    for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
    x=flag?-x:x;
}
inline void write(__int128 x){
    static char s[100];
    if (x<0)    {    putchar('-'); x=-x;}
    if(!x){ putchar('0'); return; }
    __int128 len=0; for(;x;x/=10) s[len++]=x % 10+'0';
    for(__int128 i=len-1;i>=0;--i) putchar(s[i]);
}
struct edge{
    __int128 to,next,v;
}e[200003];
__int128 head[100003];
__int128 cnt;
void init(){
    memset(head,-1,sizeof(head));
    cnt=0;
}
void add(__int128 u,__int128 v,__int128 w){
    e[cnt].to=v;
    e[cnt].next=head[u];
    e[cnt].v=w;
    head[u]=cnt++;
}
__int128 n;
__int128 siz[100003];
__int128 dp[100003];
__int128 ans[100003];
void dfs(__int128 u,__int128 fa){
    siz[u]=1;
    dp[u]=0;
    for(__int128 i=head[u];~i;i=e[i].next){
        __int128 v=e[i].to;
        if(v==fa)continue;
        dfs(v,u);
        siz[u]+=siz[v];
        dp[u]+=dp[v];
        dp[u]+=siz[v]*e[i].v;
    }
}
void dfs2(__int128 u,__int128 fa,__int128 val){
    if(fa==0)ans[u]=dp[u];
    else{
        ans[u]=ans[fa];
        ans[u]-=val*siz[u];
        ans[u]+=val*(n-siz[u]);
    }
    for(__int128 i=head[u];~i;i=e[i].next){
        __int128 v=e[i].to;
        if(v==fa)continue;
        dfs2(v,u,e[i].v);
    }
}
int main(){
    __int128 T;
    read(T);
    for(__int128 tt=1;tt<=T;tt++){
        init();
        read(n);
        __int128 u,v,w;
        for(__int128 i=1;i<n;i++){
            read(u);
            read(v);
            read(w);
            add(u,v,w);
            add(v,u,w);
        }
        dfs(1,0);
        dfs2(1,0,0);
        __int128 sum=0;
        sort(ans+1,ans+1+n);
        for(__int128 i=1;i<=n;i++)sum+=(n-i)*ans[i];
        printf("Case #");
        write(tt);
        printf(": ");
        write(sum);
        printf("\n");
    }
}

I
按左端點排序,線段樹,維護左端點所在區間右端點的最大值。如果切割的位置小於當前間右端點的最大值,暴力向下更新。注意到暴力更新的次數是O(n)的。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353;
const int maxn=200003;
int T;
int n,m;
struct seg{
    int l,r,id;
}rec[maxn];
bool cmp(seg a,seg b){
    return a.l<b.l;
}
int ord[maxn];
int ans[maxn];
int cnt;
ll res;
int mx[maxn<<2];
int clk;
void build(int u,int l,int r){
    if(l==r){
        mx[u]=rec[l].r;
        return;
    }
    int mid=(l+r)/2;
    build(2*u,l,mid);
    build(2*u+1,mid+1,r);
    mx[u]=max(mx[2*u],mx[2*u+1]);
}
void update(int u,int l,int r,int ql,int qr,int x){
    if(ql>r||qr<l)return;
    if(ql<=l&&r<=qr&&mx[u]<x)return;
    if(l==r){
        cnt++;
        res=res*rec[l].id%mod;
        mx[u]=-1e9-7;
        ans[rec[l].id]=clk;
        return;
    }
    int mid=(l+r)/2;
    update(2*u,l,mid,ql,qr,x);
    update(2*u+1,mid+1,r,ql,qr,x);
    mx[u]=max(mx[2*u],mx[2*u+1]);
}
int main(){
    scanf("%d",&T);
    for(int tt=1;tt<=T;tt++){
        memset(ans,0,sizeof(ans));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            scanf("%d%d",&rec[i].l,&rec[i].r);
            ord[i]=rec[i].l;
            rec[i].id=i;
        }
        sort(rec+1,rec+1+n,cmp);
        sort(ord+1,ord+1+n);
        build(1,1,n);
        res=0;
        clk=0;
        printf("Case #%d:\n",tt);
        for(int i=1;i<=m;i++){
            int x;
            scanf("%d",&x);
            x^=res;
            int pos=upper_bound(ord+1,ord+1+n,x)-ord-1;
            res=1;
            cnt=0;
            clk++;
            update(1,1,n,1,pos,x);
            if(cnt==0)res=0;
            printf("%d\n",cnt);
        }
        for(int i=1;i<=n;i++)printf("%d%c",ans[i],i==n?'\n':' ');
    }
}

J
注意到數據是隨機生成的,n比較大時,答案很快趨近於unsigned long long的上界。於是可以機智的做一些剪枝。

#include <bits/stdc++.h>
using namespace std;
typedef unsigned int uint;
typedef unsigned long long ll;
int n;
uint x,y,z;
inline uint tang(){
    uint t;
    x^=x<<16;
    x^=x>>5;
    x^=x<<1;
    t=x;
    x=y;
    y=z;
    z=t^x^y;
    return z;
}
uint a[10000003];
uint b[10000003];
int tot;
int main(){
    int T;
    scanf("%d",&T);
    for(int tt=1;tt<=T;tt++){
        scanf("%d%u%u%u",&n,&x,&y,&z);
        ll ans=0;
        ll one=1;
        tot=0;
        for(int i=1;i<=n;i++){
            b[i]=tang();
            if(n>=10000)
                if(b[i]<(1ll<<31)+(1<<30))continue;
            if(n>=100000)
                if(b[i]<(1ll<<31)+(1<<30)+(1<<29)+(1<<28))continue;
            a[++tot]=b[i];
            /*mx[i]=max(mx[i-1],a[i]);
            for(int j=i-1;j>=1;j--){
                if(one*mx[j]*a[i]<ans)break;
                ans=max(ans,one*a[i]*a[j]/__gcd(a[i],a[j]));
            }*/
        }
        //cout<<tot<<endl;
        sort(a+1,a+1+tot);
        for(int i=tot;i>=1;i--){
            if(one*a[i]*a[i]<ans)break;
            for(int j=i-1;j>=1;j--){
                if(one*a[j]*a[i]<ans)break;
                ans=max(ans,one*a[i]*a[j]/__gcd(a[i],a[j]));
            }
        }

        printf("Case #%d: %llu\n",tt,ans);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章