記第一場cf比賽(Codeforces915)

##比賽感想## 原本21:05開始的比賽,結果記成21:30了。。。晚了25分鐘纔開始[捂臉] 此次是Educational Round,因此還比較簡單。node

前兩道題一眼看去模擬+貪心,怕錯仔細看了好幾遍題,很快切掉 第三題,dfs+貪心 一開始想得有點簡單,少了幾種狀況,寫代碼時才發現問題…… 悲傷地發現 寫+調 這道題用了我很長時間…(這叫什麼?基礎不牢,地動山搖!)ios

而後,竟然只剩40分鐘了……c++

第四題,啊啊啊! 圖論,個人痛! 果斷跳過 第五題,額,不就是個線段樹麼? n<=10 $^9$ ?很差很差,要動態開節點 噼裏啪啦噼裏啪啦…… 提交。爲何超時了??? 個人常數真的這麼大麼?? 調一調…仍是不行。。(……比賽結束後才發現是編譯器的問題……)git

而後,竟然只剩10分鐘了……數組

第六題,啊啊啊!怎麼又是圖論! 圖論,個人痛!果斷跳過 第七題,數論,有關gcd 奇蹟般地有了思路,惋惜,時間不夠沒寫完……瀏覽器

接着,悲傷地發現比賽結束了。悲傷地發現我只作對了3道水題… 嗯,水平還有很大提高空間啊,前方路還很長……函數


##題目+題解##spa

###Codeforces 915###code

###A. Garden###排序

Luba要給花園澆水,花園長度爲k 她有n個水桶,每一個水桶一次澆的長度爲$a_i$ (不能多也不能少) 她要選擇1個水桶,使她澆得最快,且不會有地方被澆兩次,不會澆到花園外面 求她澆完的最短期。 (n,k,$a_i$ $\leq$ 100)

####想法#### 在ai中找到可整除k的最大的數,用k除以這個數即是答案

####代碼####

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

int main()
{
    int n,k,i,x,ans=1000;
    scanf("%d%d",&n,&k);
    for(i=0;i<n;i++){
        scanf("%d",&x);
        if(k%x!=0) continue;
        ans=min(ans,k/x);                 
    }
    printf("%d\n",ans);
    
    return 0;    
}

###B. Browser###

Luba在瀏覽器中打開了n個標籤,從左到右標號1~n 她只需[l,r]的標籤,因此她要把其餘的標籤關上 她的鼠標停在第pos個標籤頁上 設她某時間鼠標位置爲i,她能夠有兩種操做: 1.關閉[1,i-1]或[i+1,n]中全部開着的標籤 2.將鼠標移到第i-1或第i+1個標籤上 (前提:移到的那個標籤必須是開着的) 求她把除[l,r]外其餘標籤都關閉的最少操做數。 (n $\leq$ 100)

####想法#### 分狀況考慮+貪心

####代碼####

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

int main()
{
    int n,pos,l,r,s;
    scanf("%d%d%d%d",&n,&pos,&l,&r);
    
    if(l==1 && r==n) printf("0\n");
    else if(l==1) printf("%d\n",abs(r-pos)+1);
    else if(r==n) printf("%d\n",abs(l-pos)+1);
    else {
        s=min(abs(l-pos),abs(r-pos));
        s+=r-l+2;
        printf("%d\n",s); 
    }
    
    return 0;    
}

###C. Permute Digits###

給定a與b,求 將組成a的數字從新排列,組成的不大於b的最大整數 注意,輸出的整數與a的位數要一致,前導0要輸出 (a,b $\leq$ $10^{18}$ )

####想法#### 若b的位數比a大,那麼直接貪心,將組成a的數字從大到小輸出 不然,先把在b前補0使它與a的位數一致,接着從高位往低位考慮貪心,dfs回溯判斷是否可行,若貪心到某一位發現 a的這一位<b的這一位,後面的位直接貪心

####代碼####

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

int a[10];
char b[20],s[20];
int m,ans[20];

void Max(int cur){
    for(int i=9;i>=0;i--)
        for(int j=0;j<a[i];j++)
            ans[cur++]=i;
}
bool dfs(int cur){
    if(cur==m) return true;
    int i;
    if(a[i=b[cur]-'0']){
        ans[cur]=i;
        a[i]--;
        if(dfs(cur+1)) return true;
        a[i]++;
    }
    for(i=i-1;i>=0;i--)
        if(a[i]){
            ans[cur]=i;
            a[i]--;
            Max(cur+1);
            return true;         
        }
    return false;
}

int main()
{
    int i,n;
    scanf("%s",s);
    n=strlen(s);
    for(i=0;i<n;i++) a[s[i]-'0']++;
    scanf("%s",b);
    m=strlen(b);
    
    if(m>n) Max(0);
    else{
        for(i=m-1;i>=0;i--) b[i+n-m]=b[i];
        for(i=0;i<n-m;i++) b[i]='0';
        m=n;
        dfs(0);   
    }
    for(i=0;i<n;i++) printf("%d",ans[i]);
    printf("\n");
    
    return 0;    
}

###D. Almost Acyclic Graph###

給定一個n個點m條邊的有向圖 問是否能夠去掉一條邊是圖中再也不有環 ( 2 $\leq$ n $\leq$ 500 , 1 $\leq$ m $\leq$ min(n(n-1),100000) )

####想法#### tarjan找scc,過程當中記錄某一個環 若scc數目==n,則yes 不然,對於記錄下來的那個環,試着把每條邊刪一遍,跑tarjan 若刪掉某條邊後的scc數目==n,則yes 不然no (由於若刪一條邊知足條件的話,這條邊必定在每一個環中都出現)

####代碼####

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 505;

int dfn[N],map[N][N],low[N],vis[N],fa[N];
int scc,n,cnt;
int s[N],t;
int cir[N],tot;

void dfs(int u){
    dfn[u]=low[u]=++cnt;
    s[t++]=u; vis[u]=1;
    for(int v=1;v<=n;v++)
        if(map[u][v]){
            if(!dfn[v]){
                fa[v]=u;
                dfs(v);
                low[u]=min(low[u],low[v]);
            }
            else if(vis[v]){ 
                low[u]=min(low[u],dfn[v]);
                if(!tot){
                    for(int i=u;i!=v;i=fa[i]) cir[tot++]=i;
                    cir[tot++]=v;        
                }
            }
        }
    if(dfn[u]==low[u]){
        scc++;
        while(s[t-1]!=u) vis[s[--t]]=0;
        vis[s[--t]]=0;
    }
}
void tarjan() { for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i); }

int main()
{
    int i,m,x,y,flag;
    scanf("%d%d",&n,&m);
    for(i=0;i<m;i++)
        scanf("%d%d",&x,&y),map[x][y]=1;
    
    tarjan();
    if(scc==n) puts("YES");
    else{
        flag=0;
        y=cir[tot-1];
        for(i=0;i<tot;i++){
            x=cir[i];
            memset(dfn,0,sizeof(dfn));
            map[x][y]=0; 
            cnt=scc=0; tarjan();
            if(scc==n) { flag=1; break; }
            map[x][y]=1;
            y=cir[i];           
        }
        if(flag) puts("YES");
        else puts("NO");
    }

    return 0;    
}

###E. Physical Education Lessons###

距學期結束還有n天,Alex須要在這n天上課 但因爲他的學校上課時間常變更(共q次),因此他想知道每次變更後他須要上多少天課 有兩種變更: 1.[l,r]都不上課 2.[l,r]都要上課 假設一開始他要上n天課 ( 1 $\leq$ n $\leq$ $10^9$ , 1 $\leq$ q $\leq$ 300000 )

####想法#### 標準的線段樹啊 因爲n最大1e9因此須要動態開節點

####代碼####

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 300005;

int n;

struct node{
    node *ch[2];
    int sum,lazy;
}pool[N*50],*root;
int cnt;

void pushdown(node *p,int l,int r){
    int mid=(l+r)>>1;
    if(!p->ch[0]){
        p->ch[0]=&pool[++cnt];
        p->ch[0]->sum=(mid-l+1);     
        p->ch[0]->lazy=-1;         
    } 
    if(!p->ch[1]){
        p->ch[1]=&pool[++cnt];
        p->ch[1]->sum=(r-mid);
        p->ch[1]->lazy=-1;              
    }
    if(p->lazy!=-1){
        p->ch[0]->sum=(mid-l+1)*p->lazy;
        p->ch[1]->sum=(r-mid)*p->lazy;
        p->ch[0]->lazy=p->ch[1]->lazy=p->lazy;
        p->lazy=-1;                
    }
    
}
void update(node *p) { p->sum=p->ch[0]->sum+p->ch[1]->sum; }
void change(node *p,int l,int r,int L,int R,int k){
    if(p->sum==(r-l+1)*k) return;
    if(l==L && r==R){
        p->sum=k*(r-l+1);
        p->lazy=k;
        return;
    }
    pushdown(p,l,r);
    int mid=(l+r)>>1;
    if(mid>=R) change(p->ch[0],l,mid,L,R,k);
    else if(mid<L) change(p->ch[1],mid+1,r,L,R,k);
    else{
        change(p->ch[0],l,mid,L,mid,k);
        change(p->ch[1],mid+1,r,mid+1,R,k); 
    }
    update(p);
}

int main()
{
    int q,i,k,l,r;
    scanf("%d%d",&n,&q);
    
    root=&pool[++cnt]; root->lazy=-1;
    root->sum=n;
    
    for(i=0;i<q;i++){
        scanf("%d%d%d",&l,&r,&k);
        change(root,1,n,l,r,k-1);
        printf("%d\n",root->sum);
    }
    
    return 0;
}

###F. Imbalance Value of a Tree###

給定一棵n個節點的樹,每一個點都有權值ai 函數I(x,y)的值爲從x到y的惟一路徑上點權最大-點權最小 (包括路徑上的點x,y) 求全部點對的I(x,y)之和 (n $\leq$ $10^6$)

####想法#### 如果暴力枚舉每對點的話,就算求I爲O(1)也會超時 因而考慮函數I,其實是求每對點間路徑上的點權最小值之和 及 最大值之和 先考慮最小值 對於點權最小的那個點,顯然全部通過它的路徑上點權最小的都是它 對於點權次小的點,全部通過它且不通過點權最小點的路徑上,點權最小的都爲它 …… 對於點權次大的點,它被計算當且僅當有一條邊鏈接它與點權最大的點 對於點權最大的點,它不會被計算

因爲是樹,兩兩點間路徑是惟一的 因而就有這樣一種作法:將每條邊按照所連兩點權值較小值 從大到小排序 藉助並查集,按排好的順序合併邊所連的兩點,共n-1次 每次合併,兩個集合中各任取一點,它們之間的路徑上點權最小值爲 這條邊所連兩點權值較小值

最大值同理,只不過是按邊所連兩點權值較大值 從小到大排序

####代碼####

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 1000005;
typedef long long ll;

struct edge{
    int u,v;       
}e[N];

int val[N],fa[N],size[N];

bool cmp1(edge x,edge y) { return min(val[x.u],val[x.v])>min(val[y.u],val[y.v]); }
bool cmp2(edge x,edge y) { return max(val[x.u],val[x.v])<max(val[y.u],val[y.v]); }

int Getfa(int x) { return fa[x]==x ? x : fa[x]=Getfa(fa[x]); }

int n;

int main()
{
    int i,x,y,v;
    ll ans=0;
    scanf("%d",&n);
    for(i=1;i<=n;i++) scanf("%d",&val[i]);
    for(i=1;i<n;i++) scanf("%d%d",&e[i].u,&e[i].v);
    
    for(i=1;i<=n;i++) fa[i]=i,size[i]=1;
    sort(e+1,e+n,cmp1);
    for(i=1;i<n;i++){
        v=min(val[e[i].u],val[e[i].v]);
        x=Getfa(e[i].u); y=Getfa(e[i].v);
        if(size[x]<size[y]) swap(x,y);
        ans-=(ll)v*size[x]*size[y];
        fa[y]=x;
        size[x]+=size[y];    
    }
    
    for(i=1;i<=n;i++) fa[i]=i,size[i]=1;
    sort(e+1,e+n,cmp2);
    for(i=1;i<n;i++){
        v=max(val[e[i].u],val[e[i].v]);
        x=Getfa(e[i].u); y=Getfa(e[i].v);
        if(size[x]<size[y]) swap(x,y);
        ans+=(ll)v*size[x]*size[y];
        fa[y]=x;
        size[x]+=size[y];    
    }
    
    printf("%lld\n",ans);

    return 0;    
}

###G. Coprime Arrays###

人們管知足 gcd($a_1$,$a_2$,…,$a_n$) =1的數組a叫Coprime Array 給出n,k 設對於i $\in$ [1,k] 知足每一個元素都$\in$[1,i] 的Coprime Array的個數爲$b_i$ 求$\sum\limits_{i=1}^k$ ( $b_i$ ^ i) (n,k $\leq$ 2 $\times$ $10^6$)

####想法一####

定義對於一個數組a,lgcd=gcd($a_1$,$a_2$,…,$a_n$)

設 每一個元素都$\in$[1,i]的 知足lgcd=j 的數組個數爲w[i][j] 那麼b[i]=$i^n$-$\sum\limits_{j=2}^i$ w[i][j] 很容易發現,w[i][j]=b[$ \frac {i} {j}$ ] 接下來跟「餘數求和」有那麼一點像 愉快地超時了……

####代碼####

#include<cstdio>
#include<iostream>
#include<algorithm>

#define P 1000000007

using namespace std;

typedef long long ll;
const int N = 2000005;

int PowerMod(int x,int b){
    int ret=1;    
    while(b){
        if(b&1) ret=((ll)ret*x)%P;
        x=((ll)x*x)%P;
        b>>=1;  
    }
    return ret;
}

int f[N];
int n,k;

int main()
{
    int i,l,r,ans;
    scanf("%d%d",&n,&k);
    
    f[1]=1;
    ans=0;
    for(i=2;i<=k;i++){
        f[i]=PowerMod(i,n);
        for(l=2,r;l<=i;l=r+1){
            r=i/(i/l);
            if(r>i) r=i;
            f[i]=(f[i]-((ll)r-l+1)*(f[i/l]-P))%P; 
        }
        ans=(ans+(f[i]^i))%P;
    }
    printf("%d\n",ans);
    
    return 0;    
}

####想法二#### 想想能夠發現,b數組是遞增的 設f[i]=b[i]-b[i-1]
f[i]統計的是至少有一個元素爲i的Coprime Array個數 和想法一的思路有一點點像 設 每一個元素都$\in$[1,i]的 知足lgcd=j 且至少有一個元素爲i 的數組個數爲w[i][j] 因爲肯定數組中必定有一個數是i,那麼算出的lgcd值只能爲i的約數 很容易發現,w[i][j]=f[$\frac {i} {j} $ ] 那麼f[i]=$i^n$ - $(i-1)^n$ - $\sum${w[i][j] | j $\in$ [1,i] , i mod j=0 } (無比神奇的sum用法。。。)

####代碼####

#include<cstdio>
#include<iostream>
#include<algorithm>

#define P 1000000007

using namespace std;

typedef long long ll;
const int N = 2000005;

ll PowerMod(int x,int b){
    ll ret=1;    
    while(b){
        if(b&1) ret=(ret*x)%P;
        x=((ll)x*x)%P;
        b>>=1;  
    }
    return ret;
}

ll f[N],p[N];
int n,k;

int main()
{
    int i,j;
    ll ans,sum;
    scanf("%d%d",&n,&k);
    
    ans=sum=0;
    for(i=1;i<=k;i++){
        p[i]=PowerMod(i,n);
        f[i]=(f[i]+p[i]-p[i-1]+P)%P;
        sum=(sum+f[i])%P;
        ans=(ans+(sum^i))%P; 
        for(j=i*2;j<=k;j+=i)
            f[j]=(f[j]-f[i]+P)%P;
    }
    printf("%d\n",ans);
    
    return 0;    
}

終於……

相關文章
相關標籤/搜索