• 2019中国大学生程序设计竞赛(CCPC)


                 (7题弟弟。C题知道正解,懒得写了)

    A:^&^ HDU - 6702

    题意:给出A,B。求一个最小的C,使得min=(A^C)&(B^C)最小。

    思路:如果存在A和B都有的位,那么全选,就行了,这时结果min为0; 否则,选最小的那个,一个有,一个没有的那一位p,结果min=1<<p;

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    int T;
    unsigned int A,B,C; 
    template<class T>
    inline void read(T&a){
        char c=getchar();
        for(a=0;(c<'0'||c>'9')&&c!='-';c=getchar());
        bool f=0;if(c=='-')f=1,c=getchar();
        for(;c>='0'&&c<='9';c=getchar())a=a*10+c-'0';
        if(f)a=-a;
    }
    int main(){
        for(read(T);T--;){
            read(A),read(B);C=0;
            for(long long i=1;i<=A&&i<=B;i<<=1)
                if((i&A)&&(i&B))C|=i;
            if(!C){
                for(long long i=1;i<=A||i<=B;i<<=1)
                    if((i&A)||(i&B)){C|=i;break;}
            }
            printf("%u
    ",C);
        }
        return 0;
    }
    View Code

    B:array HDU - 6703

    题意:有两种操作,第一种单点修改。 第二种,给出(R,K),查询大于等于K的最小数,满足在a[1,R]中没有出现。 强制在线。

    思路:记录每个数出现的最小位置,把它们插入线段树。那么就是在[K,N]中找第一个对应值>R的,所以线段树保存的信息的区间最大值。 这个由于线段树自带二分功能,即如果作区间有Mx>R的,那么在左区间找答案,是否在右区间找;  set保存相同数的最小位置; 所以整体的复杂度就是O(NlogN);

    #include<bits/stdc++.h>
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    const int maxn=400010;
    int a[maxn],Mx[maxn],RR;
    set<int>s[maxn];
    set<int>::iterator it;
    void read(int &x){
        x=0; char c=getchar();
        while(c>'9'||c<'0') c=getchar();
        while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    }
    void update(int Now,int L,int R,int pos,int val)
    {
        if(L==R){
            Mx[Now]=val; return ;
        }
        int Mid=(L+R)>>1;
        if(pos<=Mid) update(Now<<1,L,Mid,pos,val);
        else update(Now<<1|1,Mid+1,R,pos,val);
        Mx[Now]=max(Mx[Now<<1],Mx[Now<<1|1]);
    }
    int query(int Now,int L,int R,int l,int r,int RR)
    {
        int Mid=(L+R)>>1,res=0;
        if(Mx[Now]<=RR) return 0;
        if(l<=L&&r>=R) {
            if(L==R&&Mx[Now]>RR) return L;
            if(Mx[Now<<1]>RR) return query(Now<<1,L,Mid,l,r,RR);
            if(Mx[Now<<1|1]>RR) return query(Now<<1|1,Mid+1,R,l,r,RR);
            return 0;
        }
        if(l<=Mid) res=max(res,query(Now<<1,L,Mid,l,r,RR));
        if(res) return res;
        if(r>Mid) res=max(res,query(Now<<1|1,Mid+1,R,l,r,RR));
        return res;
    }
    int main()
    {
        int T,N,M,ans,opt,K,pos;
        scanf("%d",&T);
        while(T--){
            ans=0; scanf("%d%d",&N,&M);
            rep(i,1,N) s[i].clear();
            rep(i,1,N<<2) Mx[i]=0;
            rep(i,1,N) read(a[i]);
            rep(i,1,N) s[a[i]].insert(i);
            rep(i,1,N){
                if(s[i].empty()) continue;
                int t=*s[i].begin();
                update(1,1,N,i,t);
            }
            rep(i,1,M){
                read(opt);
                if(opt==1){
                    read(pos); pos^=ans;
                    if(a[pos]<=N) {
                        it=s[a[pos]].lower_bound(pos);
                        if(it==s[a[pos]].begin()){
                            s[a[pos]].erase(it);
                            int tmp=N+1;
                            if(!s[a[pos]].empty()) tmp=*s[a[pos]].begin();
                            if(tmp==N+1) update(1,1,N,a[pos],N+1);
                            else update(1,1,N,a[pos],tmp);
                        }
                        else s[a[pos]].erase(it);
                        a[pos]+=10000000;
                    }
                }
                else {
                    read(RR); read(K);
                    RR^=ans; K^=ans;
                    ans=query(1,1,N,K,N,RR);
                    if(ans==0) ans=N+1;
                    printf("%d
    ",ans);
                }
            }
        }
        return 0;
    }
    View Code

    C:K-th occurrenceHDU - 6704

    题意:给定字符串S,Q次询问,每次给出(L,R,K),让你求S中 str[L,R]这个子串第K次出现的位置。

    思路:显然是SA的区间问题,可以RMQ出一个区间,满足这个区间的min(ht)>=R-L+1;然后在这个区间主席树求第K大。

    代码,稍等两天。

    update-8-30 : 作为一个sam的真粉,最终还是用sam把这题补了。

    前置知识点:1,线段树的启发式合并,维护每个节点的endpos。  2,倍增可以快速地转移到某个子串在sam中的位置。

    3,线段树可以二分第K个数。

    补了两个CF的题,可以对照一下:

    CodeForces - 666E: Forensic Examination (SAM 线段树合并)

    CodeForces - 1037H: Security(SAM+线段树合并)

    #include<bits/stdc++.h>
    #define rep2(i,a,b) for(int i=a;i>=b;i--)
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    const int maxn=200010;
    char c[maxn];
    int rt[maxn],pos[maxn],N,Q,tot,f[maxn][20];
    struct in{
        int L,R,sum;
    }s[maxn*90];
    void ins(int &now,int L,int R,int p)
    {
        if(!now) now=++tot;
        s[now].sum++; if(L==R) return ;
        int Mid=(L+R)>>1;
        if(p<=Mid) ins(s[now].L,L,Mid,p);
        else ins(s[now].R,Mid+1,R,p);
    }
    void merge(int &now,int x,int y,int L,int R)
    {
        if(!x||!y) { now= x|y; return ;}
        now=++tot;
        if(L==R) {
            s[now].sum=s[x].sum+s[y].sum;
            return ;
        }
        int Mid=(L+R)>>1;
        merge(s[now].L,s[x].L,s[y].L,L,Mid);
        merge(s[now].R,s[x].R,s[y].R,Mid+1,R);
        s[now].sum=s[s[now].L].sum+s[s[now].R].sum;
    }
    int query(int Now,int L,int R,int K)
    {
        if(s[Now].sum<K) return -1;
        if(L==R) return L;
        int Mid=(L+R)>>1;
        if(s[s[Now].L].sum>=K) return query(s[Now].L,L,Mid,K);
        return query(s[Now].R,Mid+1,R,K-s[s[Now].L].sum);
    }
    struct SAM{
        int ch[maxn][26],fa[maxn],maxlen[maxn],cnt,last;
        void init()
        {
            rep(i,1,tot) s[i].L=s[i].R=s[i].sum=0;
            rep(i,0,cnt) G[i].clear(),rt[i]=0;
            cnt=last=1; tot=0;
            memset(ch[1],0,sizeof(ch[1]));
        }
        void add(int x,int id)
        {
            int np=++cnt,p=last; last=np; ins(rt[np],1,N,id);
            maxlen[np]=maxlen[p]+1; memset(ch[np],0,sizeof(ch[np]));
            while(p&&!ch[p][x]) ch[p][x]=np,p=fa[p];
            if(!p) fa[np]=1;
            else {
                int q=ch[p][x];
                if(maxlen[q]==maxlen[p]+1) fa[np]=q;
                else {
                   int nq=++cnt; maxlen[nq]=maxlen[p]+1;
                   fa[nq]=fa[q]; fa[q]=fa[np]=nq;
                   memcpy(ch[nq],ch[q],sizeof(ch[q]));
                    while(p&&ch[p][x]==q) ch[p][x]=nq,p=fa[p];
                }
            }
        }
        vector<int>G[maxn];
        void dfs(int u)
        {
            for(int i=0;i<G[u].size();i++){
                int v=G[u][i];
                f[v][0]=u; dfs(v);
                merge(rt[u],rt[u],rt[v],1,N);
            }
        }
        void DFS()
        {
            rep(i,2,cnt) G[fa[i]].push_back(i);
            dfs(1);
            rep(i,1,18) rep(j,1,cnt) f[j][i]=f[f[j][i-1]][i-1];
        }
    }T;
    void solve()
    {
        int L,R,K,len;
        scanf("%d%d%d",&L,&R,&K);
        len=R-L+1; int now=pos[R];
        for(int i=18;i>=0;i--)
            if(T.maxlen[f[now][i]]>=len) now=f[now][i];
        int ans=query(rt[now],1,N,K);
        if(ans!=-1) ans=ans-len+1;
        printf("%d
    ",ans);
    }
    int main()
    {
        int Case;
        scanf("%d",&Case);
        while(Case--){
            T.init();
            scanf("%d%d",&N,&Q);
            scanf("%s",c+1);
            rep(i,1,N) T.add(c[i]-'a',i),pos[i]=T.last;
            T.DFS();
            while(Q--) solve();
        }
        return 0;
    }
    View Code

    D:path HDU - 6705

    题意:给定有向图,然后对走路没有限制,包括起点,终点,是否重复走,都是自由的。 求第K长路径。 K<=5e4;(内存比较小)

    思路:显然是个比较水的BFS,可以用优先队列来搞,但是内存不够。所以用multiset,这样如果set.size()>K,可以删除尾巴。 看似O(NlogN)的复杂度,却卡了。

    是这样的,如果菊花图,边比较多,那么复杂度就接近(KM)了,显然GG,那么我们可以把后续节点按照边权排序,然后每次取前面几个小的即可。

    (即是set里面可以删去尾巴,图里面的G[]的尾巴也可以删一部分。不然很容易GG。

    #include<bits/stdc++.h>
    #define f first
    #define s second
    #define ll long long
    #define pii pair<ll,int>
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    const int maxn=250010;
    int Mx,tot,N,fcy[maxn]; ll ans[maxn];
    vector<pair<int,int> >G[maxn];
    void read(int &x){
        x=0; char c=getchar();
        while(c>'9'||c<'0') c=getchar();
        while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    }
    void SPFA()
    {
        multiset<pii>q; tot=0;ll FCY=0;int SZ=0;
        multiset<pii>::iterator it;
        rep(i,1,N) {
            for(int j=0;j<G[i].size();j++){
               q.insert(make_pair(G[i][j].first,G[i][j].second)); SZ++;
               if(SZ>Mx) q.erase(--q.end()),SZ--;
            }
        }
        while(SZ){
            pii now=*q.begin(); int u=now.second;
            q.erase(q.begin()); SZ--;
            ans[++tot]=now.first;
            if(tot>=Mx) return ;
            for(int i=0;i<G[u].size();i++){
                if(SZ&&SZ+tot==Mx&&now.f+G[u][i].f>(*(--q.end())).f) break;
                int v=G[u][i].second;
                q.insert(pii{now.first+G[u][i].first,v}); SZ++;
                while(SZ>Mx-tot) q.erase(--q.end()),SZ--;
            }
        }
    }
    int main()
    {
        int T,M,Q,u,v,w;
        scanf("%d",&T);
        while(T--){
             scanf("%d%d%d",&N,&M,&Q);
             rep(i,1,N) G[i].clear(); Mx=0;
             rep(i,1,M){
                 read(u); read(v); read(w);
                 G[u].push_back(make_pair(w,v));
             }
             rep(i,1,N) sort(G[i].begin(),G[i].end());
             rep(i,1,Q) {
                 read(fcy[i]);
                 Mx=max(Mx,fcy[i]);
             }
             SPFA();
             rep(i,1,Q) printf("%lld
    ",ans[fcy[i]]);
        }
        return 0;
    }
    View Code

    E: huntian oy HDU - 6706

    题意:求题意中的互质对数量。

    思路:反演一下。

    #include <bits/stdc++.h>
    #include<tr1/unordered_map>
    #define Accepted 0
    using namespace std;
    typedef long long ll;
    const int MOD = 1e9 + 7;
    const int maxn = 5e6 + 10;
    bool not_prime[maxn];
    int prime[maxn / 10];
    ll phi[maxn];
    void init()
    {
        int n = 5000000;
        int tot = 0;
        not_prime[1] = 1;
        phi[1] = 1;
        for(int i = 2; i <= n; i++)
        {
            if(!not_prime[i])prime[tot++] = i, phi[i] = i - 1;
            for(int j = 0; j < tot && 1LL * prime[j] * i <= n; j++)
            {
                not_prime[prime[j] * i] = 1;
                if(i % prime[j] == 0)
                {
                    phi[i * prime[j]] = phi[i] * prime[j];
                    break;
                }
                else
                {
                    phi[i * prime[j]] = phi[i] * (prime[j] - 1);
                }
            }
        }
        phi[1] = 1;
        for(ll i = 2; i <= n; i++)
            phi[i] = (i * phi[i] % MOD + phi[i - 1]) % MOD;
    }
    tr1::unordered_map<ll, ll>sumphi;
    const ll inv2 = 500000004;
    const ll inv6 = 166666668;
    ll Sum(ll x)
    {
        if(x <= 5000000)return phi[x];
        if(sumphi[x])return sumphi[x];
        ll ans = x * (x + 1) % MOD * (2 * x + 1) % MOD * inv6 % MOD;
        for(ll l = 2; l <= x; l++)
        {
            ll r = x / (x / l);
            ll tmp = (l + r) * (r - l + 1) % MOD * inv2 % MOD * Sum(x / l) % MOD;
            ans = (ans + MOD - tmp) % MOD;
            l = r;
        }
        return sumphi[x] = ans;
    }
    int main()
    {
        init();
        int T;
        scanf("%d", &T);
        while(T--)
        {
            ll n, a, b, ans = 0;
            scanf("%lld%lld%lld", &n, &a, &b);
            printf("%lld
    ", (Sum(n) + MOD - 1) * inv2 % MOD);
        }
        return Accepted;
    }
    View Code

    F:Shuffle Card HDU - 6707

    题意:给定N个数的排列,然后M次操作,每次把给定的数放到排列的最前面。求最后的排列。

    思路:如果一个数多次放到前面,那么最后一次是有影响的操作。 所以我们倒叙操作,维护一个queue,如果是第一次操作,就处理,放到队尾。然后把没有操作过的数按原来顺序插入即可。

    #include<bits/stdc++.h>
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    const int maxn=200010;
    int Laxt[maxn],Next[maxn],To[maxn],cnt;
    void add(int u,int v)
    {
        Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v;
    }
    int a[maxn],vis[maxn],p[maxn],ans[maxn],tot;
    int main()
    {
        int T,N,M;
        scanf("%d%d",&N,&M);
        rep(i,1,N) scanf("%d",&a[i]);
        rep(i,1,M) scanf("%d",&p[i]);
        for(int i=M;i>=1;i--){
            if(vis[p[i]]) continue;
            ans[++tot]=p[i];
            vis[p[i]]=1;
        }
        rep(i,1,N) if(!vis[a[i]]) ans[++tot]=a[i];
        rep(i,1,N) printf("%d ",ans[i]);
        return 0;
    }
    View Code

    G: Windows Of CCPC HDU - 6708

    签到题。

    #include <bits/stdc++.h>
    #define Accepted 0
    using namespace std;
    typedef long long ll;
    const int maxn = 1e5 + 10;
    bool s[1030][1030];
    
    void dfs(int x, int y, int d, bool f)
    {
        if(d == 2)
        {
            if(f)
            {
                s[x][y] = s[x][y + 1] = s[x + 1][y + 1] = 1;
                s[x + 1][y] = 0;
            }
            else
            {
                s[x][y] = s[x][y + 1] = s[x + 1][y + 1] = 0;
                s[x + 1][y] = 1;
            }
        }
        else
        {
            d /= 2;
            dfs(x, y, d, f);
            dfs(x, y + d, d, f);
            dfs(x + d, y, d, !f);
            dfs(x + d, y + d, d, f);
        }
    }
    int main()
    {
        int T, n;
        scanf("%d", &T);
        while(T--)
        {
            scanf("%d", &n);
            dfs(0, 0, (1 << n), 1);
            for(int i = 0; i < (1 << n); i++)
            {
                for(int j = 0; j < (1 << n); j++)
                    if(s[i][j])putchar('C');
                    else putchar('P');
                putchar('
    ');
            }
        }
        return Accepted;
    }
    View Code

    H: Fishing Master HDU - 6709

    题意:有一个锅,一个鱼竿,钓鱼的时间的固定的K,每个鱼煮的时间是a[]。 同时最多钓一个鱼,同时最多煮一个鱼,问最短的时间,把鱼都钓起来煮好。

    思路:按时间倒叙排序。 然后处理就行了。  但是做着会发现,这样显然还不够。 我们需要想办法把a[]%K之后在操作。

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    int T,n,k,t[100010],cnt;
    long long ans;
    template<class T>
    inline void read(T&a){
        char c=getchar();
        for(a=0;(c<'0'||c>'9')&&c!='-';c=getchar());
        bool f=0;if(c=='-')f=1,c=getchar();
        for(;c>='0'&&c<='9';c=getchar())a=a*10+c-'0';
        if(f)a=-a;
    }
    int main(){
        for(read(T);T--;){
            ans=cnt=0;read(n);read(k);
            for(register int i=1;i<=n;i++){
                read(t[i]),ans+=t[i]+k;
                cnt+=t[i]/k;t[i]%=k;
            }
            ans-=(long long)k*min(cnt,n-1);
            int j=n-1-min(cnt,n-1);
            sort(t+1,t+1+n);
            for(int i=n;i>=n-j+1;i--)ans-=t[i];
            printf("%lld
    ",ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    POJ 1611 The Suspects 并查集
    POJ A Simple Problem with Integers 线段树的成段更新
    POJ 2367 Genealogical tree 拓扑排序
    《大道至简》读后感
    周总结报告
    周学习进度总结
    周总结报告
    周总结报告
    周总结报告
    教室派评价
  • 原文地址:https://www.cnblogs.com/hua-dong/p/11407399.html
Copyright © 2020-2023  润新知