2020CCPC網絡賽


在比賽開始30分鍾內依次A了10,7,3,一個小時左右A了11,然后就再也沒有寫出任何一道題。。。

其實比賽過程中心態有很大的問題,我一直在5上面鑽牛角尖,耗費了太多時間。

基本上比賽后半程三個人都在看2,5兩道題,其他題目幾乎沒看。

到最后基本沒有做下去的欲望了,可能是因為是女隊壓力比較小吧(不,是因為我比較菜)。。

以及網絡賽的排行榜真的很迷。。第2題min25篩一堆人過。。然而隊里三個人甚至都沒有聽說過這玩意。。

(226大佬7題,ORZ)

1002 Graph Theory Class

題意:在一個有n個點的圖中,圖中點1~n編號。對於i,j之間的一條邊,它的權重為lcm(i+1,j+1),求這個圖的最小生成樹大小。

思路:很明顯對於(j+1)為質數的情況,j應該與1連接,權重為2*(j+1);

對於(j+1)不是質數的情況,j可以與任意一個滿足i+1==factor(j+1)的i連接,factor(j+1)指j+1的因子,權重為j+1。

於是最小生成樹的大小為(3~n+1以內所有質數)*2+(3~n+1以內所有非合數),即(3~n+1所有數之和)+(3~n+1以內所有質數之和)

前者可以用等差數列求和計算,(n+4)*(n-1)/2;由於n<=1010,后者可以使用min25篩計算。

min25篩,可以在O(n3/4/logn)的時間內求積性函數f(x)的前綴和。

(積性函數,指對於所有互質的整數a和b有性質f(ab)=f(a)f(b)的函數)

(代碼待補充

1003 Express Mail Taking

題意:有n個保險櫃,編號為1~n,每兩個相鄰編號的保險櫃之間的距離為1,第k個保險櫃用來打開其他保險櫃(每次只能同時打開一個保險櫃)。從1出發,需要從m個保險櫃中取東西后再從1離開。問距離最短的路線長度。

思路:一開始理解錯了題意,以為只要到達第k保險櫃后其他保險櫃就打開了,就WA了一次。

對於m個要取東西的櫃子,除了最后一個櫃子有可能不需要返回k櫃,對於其他櫃子,都必須在該櫃子與k櫃之間走一個來回。

於是找到m個櫃子中最小的一個x,若x比k小,則將該點刪去(在回1點的路上就可到達,對距離無貢獻)

接着對剩下的每個櫃子i,貢獻2*abs(i-k)的答案。

再加上出發和結束的2*(k-1),就是答案。

#include<cstdio>
#include<cstring>
#include<algorithm> 
using namespace std;
typedef long long ll;
const int maxn=2000005;
int a[maxn];
int main()
{
    int i,T,n,m,k,mi;
    ll ans;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d%d",&n,&m,&k);
        ans=2ll*(k-1);
        mi=k;
        for (i=1;i<=m;i++) 
        {
            scanf("%d",&a[i]);
            ans+=2ll*abs(a[i]-k);
            if (a[i]<mi) mi=a[i];
        }
        if (mi!=k) ans-=2ll*abs(mi-k);
        printf("%lld\n",ans);
     } 
    return 0;
} 
View Code

1005 Lunch

題意:有n個數,兩人輪流進行操作,每次操作能將其中一個數x變為k*(x/k),此處的k必須是x的因子且k!=x。當場上的數均為1時游戲結束,當前操作方輸。問先手可不可以獲勝。

思路:(看了半天別人的題解還是有點暈乎乎的)

觀察發現,對於一個數,分成偶數個會增加偶數個操作,於是這樣不會使得最后的勝負情況發生改變。

分成奇數個則剛好相反,會改變最后的勝負情況。

把數x分解,x就變成了一堆質因子的乘積。

()

因為每次操作要讓這個數x變成自己的因數,所以每次必須取走x的至少一個質因子,

那么就可以將這個數的所有質因子(考慮重復,因為假如你有2個質因子3,那么是可以分開取,所以重復的而要考慮)當作一堆石子的個數。

而其中的2它比較特殊,因為它是偶數,不會使最后的勝負情況發生改變。

於是不管分幾次取2,都相當於直接把2全取走。但是又必須要取走2,所以就把所有的因數2當作一個石子一起取走。

(以上來自隊友的分析)

然后就變成了nim博弈,即有n堆石子,兩人輪流進行操作,每次可從任意一堆石子里取走任意多枚石子,不能不取,問先手必勝還是必敗。

對於答案,求每一堆的石子數的異或和,若為0則必敗,否則必勝。

(然后不知道為什么T了很久,好像也不是讀入優化的問題啊。。。

補充:對於nim答案的分析:對於一堆石子,只要石子數不為0,直接拿走所有石子就贏了,所以必勝。

對於兩堆石子,如果異或和為0則必敗。此時兩堆石子數相同,對方只要按照先手方對一堆石子的操作,對另一堆石子上進行同樣的操作,就能夠勝利。

如果異或和不為0則必勝。先手方可以先將一堆石子變為和另一堆石子一樣,然后就變成了剛才所說的局面,異或和為0。

......

對於很多堆石子,如果異或和為0必敗;否則改變其中最大的那堆的大小,一定能使剩下的局面的異或和為0。

(局面異或和為0的具體操作:對手對一堆進行操作,其他堆中一定有某一堆,可以使先手對它做出相同的操作。

#include<cstdio>
#include<cstring>
#include<algorithm> 
using namespace std;
int cnt,vis[32005],prime[5005];
int div(int x)
{
    int re=0;
    while (!(x&1))
    {
        x>>=1;
        re=1;
    }
    for (int i=2;i<=cnt && 1ll*prime[i]*prime[i]<=x;i++)
    {
        while (x%prime[i]==0)
        {
            x=x/prime[i]; 
            re++;
        }
    } 
    if (x!=1) re++;
    return re;
}
void pri(int n)
{
    cnt=0;
    memset(vis,0,sizeof(vis));
    for (int i=2;i<n;i++)
    {
        if (!vis[i]) prime[++cnt]=i;
        for (int j=1;j<=cnt && 1ll*i*prime[j]<n;j++)
        {
            vis[i*prime[j]]=1;
            if (i%prime[j]==0) break;
        }
    }
}
int main()
{
    int T,n,ans,x;
    pri(32000);
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d",&n);
        ans=0;
        for (int i=0;i<n;i++) 
        {
            scanf("%d",&x);
            ans^=div(x);
        }
        if (ans) printf("W\n");
        else printf("L\n");
    } 
    return 0;
} 
View Code

1006 Robotic Class

題意:給一個有n點的圖,點i有k[i]條出邊,若通過i的一條出邊j,就會使當前的權值x變為c[i][j]*x+b[i][j],並走到下一點d[i][j]。對於有多條出邊的點i,每次會選擇一條滿足a[i][j]x並且編號j最小的路徑j。現在從任一點s出發,初始權值為x0,走到n點停止,最后到達n點的權值為Cs(x0),問對於每一個s,Cs(x)是否均為連續。

思路:看完了別人的代碼,感覺這題沒做出來有點虧。。

因為f(x)連續,所以若在i點的權值為a[i][j]-與a[i][j]+那么兩者到達n點得到的權值應當相同。

所以對於每一個點i的a[i][j],模擬以初始權值x0=a[i][j]-與a[i][j]+從i出發直到n的路徑,檢查最后得到的Ci(a[i][j]-)與Ci(a[i][j]+)是否相等。

具體見代碼。

#include<cstdio>
#include<cstring>
#include<algorithm> 
using namespace std;
typedef long long ll;
const int maxn=505;
const int maxm=2005;
int n,k[maxn],a[maxn][maxm],b[maxn][maxm],c[maxn][maxm],d[maxn][maxm];
ll dfs(int t,ll x,int mark) 
{
    if (t==n) return x;
    int pos=lower_bound(a[t],a[t]+k[t],x)-a[t];
    if (pos<k[t] && x==a[t][pos] && mark>0) pos++;
    int now=0;
    if (c[t][pos]>0) now=1;
    else if (c[t][pos]<0) now=-1;
    return dfs(d[t][pos],1ll*c[t][pos]*x+b[t][pos],now*mark);
}
int main()
{
    int t,T,i,j,flag;
    ll l,r;
    scanf("%d",&T);
    for (t=1;t<=T;t++)
    {
        scanf("%d",&n);
        for (i=1;i<n;i++)
        {
            scanf("%d",&k[i]);
            for (j=0;j<k[i];j++) scanf("%d%d%d%d",&c[i][j],&b[i][j],&d[i][j],&a[i][j]);
            scanf("%d%d%d",&c[i][j],&b[i][j],&d[i][j]);
        }
        flag=1;
        for (i=1;i<n;i++) 
        {
            for (j=0;j<k[i];j++) 
            {
                l=dfs(i,a[i][j],-1);
                r=dfs(i,a[i][j],1);
                if (l!=r) 
                {
                    flag=0;
                    break;
                }
            }
            if (!flag) break;
        }
        printf("Case #%d: ",t);
        if (flag) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
} 
View Code

1007 CCPC Training Class

題意:花里胡哨的題面。

思路:是一題題面比較長的簽到題。。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int t,a[30],mx,pos,cs,l;
char s[100010];

int main()
{
    scanf("%d",&t);
    while (t--)
    {
        scanf("%s",s+1);
        l=strlen(s+1);
        mx=0;
        memset(a,0,sizeof(a));
        for (int i=1;i<=l;i++)
            a[s[i]-'a']++;
        for (int i=0;i<26;i++)
            mx=max(mx,a[i]);
        printf("Case #%d: %d\n",++cs,mx);
    }
    return 0;
}
比賽時代碼

1010 Reports

題意:就是每次輸入一個長度為n的01串,若存在相鄰兩位一樣就輸出NO,否則輸出YES。

思路:這么短的題目顯然是簽到題啦,照題意做就行了。

#include<cstdio>
using namespace std;
int t,y,x,op,n;

int main()
{
    scanf("%d",&t);
    while (t--)
    {
        scanf("%d",&n);
        scanf("%d",&y);
        op=1;
        for (int i=1;i<n;i++)
        {
            scanf("%d",&x);
            if (x==y) op=0;
            y=x;
        }
        if (!op) puts("NO");
        else puts("YES");
    }
    return 0;
}
折服於隊友的手速

1011 3x3 Convolution

題意:給出n*n矩陣A和3*3矩陣K(n>=3), 定義n*n矩陣C(A,K),它的每一項Cx,y滿足

 定義Cm(A,K)=C(Cm1(A,K),K) and C1(A,K)=C(A,K)求limtCt(A,K)。

思路:觀察樣例,發現樣例中答案只有兩種,原矩陣和零矩陣。

經過一番胡亂分析,推測當輸入的K矩陣只有左上角有不為0的數,其他位置都是0時,輸出原矩陣;其他情況輸出零矩陣,

然后跟隊友說了猜想,隊友就A了,A得我一頭霧水,果然像學長說的一樣,大膽猜想,不用證明(bushi)。

以及這題的輸出格式真的。。。我寫的時候PE了一片,(然而我的隊友一發就過了。。

補充:C1,1=A1,1*K1,1+A1,2*K1,2+......+A3,2*K3,2+A3,3*K3,3

......

Cn,n-1=An,n-1*K1,1+An,n*K1,2

Cn,n=An,n*K1,1

然后進行分類討論

1、如果K1,1==0,Cn,n就為0;接下來的C2中的Cn,n-1也會變成0,以此類推,最后矩陣Ct會變成零矩陣。

2、如果K1,1!=0,K其他位都為0,那么矩陣中的所有項相當於乘上了K1,1的t次方,各項之間的比值不變,於是答案是原矩陣。

3、如果K1,1!=0,K其他位中存在不為0的項,因為K1,1<1Cn,n會不斷變小,直至無限趨近於0

推理可得,Ct中的Cn,n-1=An,n-1*Kt1,1+t*An,n*Kt-11,1*K1,2

由於K所有項之和為1,K1,1與K1,2都小於1,分析可得當t∞時,Ct中的該項趨近於0;

同理,其他項在Ct中也都趨近於0,於是最后得到的Ct是零矩陣。

#include<cstdio>
#include<cstring>
#include<algorithm> 
using namespace std;
typedef long long ll;
const int maxn=52;
int a[maxn][maxn];
int main()
{
    int i,j,T,n,flag,x;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d",&n);
        for (i=1;i<=n;i++)
        for (j=1;j<=n;j++) scanf("%d",&a[i][j]);
        flag=0;
        for (i=1;i<=3;i++)
        for (j=1;j<=3;j++) 
        {
            scanf("%d",&x);
            if (x)
            {
                if (i==1 && j==1) flag=1;
                else flag=0;
            }
        }    
        if (flag) 
        {
            for (i=1;i<=n;i++)
            for (j=1;j<=n;j++) 
            {
                printf("%d",a[i][j]);
                if (j==n) printf("\n");
                else printf(" ");
            }
        }    
        else 
        {
            for (i=1;i<=n;i++)
            for (j=1;j<=n;j++) 
            {
                printf("0");
                if (j==n) printf("\n");
                else printf(" ");
            }
        }
    }
    return 0;
}
這是我寫的
#include<cstdio>
#define N 55
using namespace std;
int t,n,a[N][N],b[N][N],sum;

int main()
{
    scanf("%d",&t);
    while (t--)
    {
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                scanf("%d",&a[i][j]);
        sum=0;
        for (int i=1;i<=3;i++)
            for (int j=1;j<=3;j++)
                scanf("%d",&b[i][j]),sum+=b[i][j];
        if (b[1][1]==sum)
            for (int i=1;i<=n;i++)    
                for (int j=1;j<=n;j++)    
                    printf("%d%c",a[i][j]," \n"[j==n]);
        else
            for (int i=1;i<=n;i++)
                for (int j=1;j<=n;j++)
                    printf("0%c"," \n"[j==n]);
    }
    return 0;
}
這是比賽的時候隊友交的

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM