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\n",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\n",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\n",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\n",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\n", (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('\n');
        }
    }
    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\n",ans);
    }
    return 0;
}
View Code

 

posted @ 2019-08-25 11:49  nimphy  阅读(1566)  评论(0编辑  收藏