zoukankan      html  css  js  c++  java
  • XVII Open Cup named after E.V. Pankratiev. GP of Two Capitals

    A. Artifact Guarding

    选出的守卫需要满足$max(a+b)leq sum a$,从小到大枚举每个值作为$max(a+b)$,在权值线段树上找到最大的若干个$a$即可。

    时间复杂度$O(nlog n)$。

    #include<cstdio>
    #include<algorithm>
    #include<set>
    #include<map>
    using namespace std;
    typedef long long ll;
    const int N=100010,M=262150,inf=~0U>>1;
    int n,i,ans;
    int cnt[M];ll sum[M];
    ll b[N];
    struct P{ll x,y;}a[N];
    inline bool cmp(const P&a,const P&b){return a.x<b.x;}
    inline int lower(ll x){
        int l=1,r=n,mid,t;
        while(l<=r)if(b[mid=(l+r)>>1]<=x)l=(t=mid)+1;else r=mid-1;
        return t;
    }
    void ins(int x,int a,int b,int c,ll p){
        cnt[x]++;
        sum[x]+=p;
        if(a==b)return;
        int mid=(a+b)>>1;
        if(c<=mid)ins(x<<1,a,mid,c,p);else ins(x<<1|1,mid+1,b,c,p);
    }
    inline int ask(ll k){
        if(k>sum[1])return inf;
        int x=1,a=1,b=n,mid,t=0;
        while(a<b){
            mid=(a+b)>>1;
            if(k<=sum[x<<1|1]){
                a=mid+1;
                x=x<<1|1;
            }else{
                k-=sum[x<<1|1];
                t+=cnt[x<<1|1];
                b=mid;
                x<<=1;
            }
        }
        return t+k/(::b[a])+(k%(::b[a])>0);
    }
    int main(){
        freopen("artifact.in","r",stdin);
        freopen("artifact.out","w",stdout);
        scanf("%d",&n);
        for(i=1;i<=n;i++){
            ll x,y;
            scanf("%lld%lld",&x,&y);
            a[i].x=x+y;//b
            a[i].y=x;//a
            b[i]=x;
        }
        sort(a+1,a+n+1,cmp);
        sort(b+1,b+n+1);
        ans=inf;
        for(i=1;i<=n;i++){
            ins(1,1,n,lower(a[i].y),a[i].y);
            ans=min(ans,ask(a[i].x));
        }
        if(ans==inf)ans=-1;
        printf("%d",ans);
    }
    

      

    B. Book Pages

    将行中的空格以及#号都去掉,那么某一行$A$对应原著中某一行$B$的条件是$A$是$B$的子序列,且$A$与$B$串长差不多。

    从上到下考虑原著中的每一行,维护一个可能的页码集合,从中逐渐淘汰掉失配的即可。

    时间复杂度$O(n^2)$。

    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<vector>
    #include<iostream>
    using namespace std;
    const int N=10010;
    char a[7000010];
    int i,j,k,n;
    int q[N],q2[N],ans[N],pos[N];
    int cnt,cnt2,lim;
    vector<string>b[N];
    inline bool valid(char x){
        if(x==' ')return 0;
        if(x>=32&&x<=126)return 1;
        if(x=='#')return 1;
        return 0;
    }
    inline bool ispar(string t){
        if(t.size()<70)return 0;
        return t[0]=='-'&&t[1]=='-'&&t[2]=='-'&&t[3]=='#'&&t[4]=='#'&&t[5]=='#'
             &&t[6]=='-'&&t[7]=='-'&&t[8]=='-'&&t[9]=='#'&&t[10]=='#'&&t[11]=='#';
    }
    inline void init(){
        int i;
        cnt=0;
        for(i=1;i<=n;i++)if(!ans[i])q[++cnt]=i,pos[i]=0;
    }
    inline bool check(string A,int x){
        if(pos[x]>=b[x].size())return 0;
        int o=pos[x]++;
        if(b[x][o].size()>A.size())return 0;
        if(b[x][o].size()+10<A.size())return 0;
        for(int i=0,j=0;i<b[x][o].size();i++){
            while(j<A.size()&&A[j]!=b[x][o][i])j++;
            if(j==A.size())return 0;
            j++;
        }
        return 1;
    }
    int main(){
        freopen("book-pages.in","r",stdin);
        freopen("book-pages.out","w",stdout);
        while(gets(a+1)){
            int len=strlen(a+1);
            int l=1,r=len;
            while(l<=r&&!valid(a[l]))l++;
            while(l<=r&&!valid(a[r]))r--;
            //for(i=l;i<=r;i++)putchar(a[i]);
            if(l>r)continue;
            string t="";
            for(int i=l;i<=r;i++)t.push_back(a[i]);
            if(ispar(t)){
                //newpage
                n++;
                continue;
            }
            t="";
            bool flag=0;
            for(int i=l;i<=r;i++)if(a[i]!=' '&&a[i]!='#')t.push_back(a[i]),flag=1;
            if(!flag)continue;
            b[n].push_back(t);
        }
        /*for(i=0;i<=n;i++){
            for(j=0;j<b[i].size();j++)cout<<b[i][j]<<endl;
            cout<<"--END--"<<endl;
        }
        puts("DONE");*/
        for(i=1;i<=n;i++)ans[i]=0;
        init();
        for(i=0;i<b[0].size();i++){
            cnt2=0;
            int flag=0;
            for(j=1;j<=cnt;j++){
                int x=q[j];
                //printf("check %d %d %d
    ",i,x,check(b[0][i],x));
                if(check(b[0][i],x)){
                    if(pos[x]==b[x].size()){
                        flag=x;
                        break;
                    }
                    //puts("PUSH");
                    q2[++cnt2]=x;
                }
            }
            //printf("cnt2=%d
    ",cnt2);
            if(flag){
                ans[flag]=++lim;
                init();
            }else{
                for(j=1;j<=cnt2;j++)q[j]=q2[j];
                cnt=cnt2;
            }
        }
        for(i=1;i<=n;i++)printf("%d%c",ans[i],i<n?' ':'
    ');
    }
    /*
    rehuitg vnudfj rei436 tgrh
    rtyhtr 43 m98t43 m9
    43645784 9hgfuh fg
    235u4398654u9867
    g45uy 985498hj54h98yj6
    2543y89675498 ghjf8rghfd
    436u8934 89rghjre98 gynh9854
    hj985hj5849b y68947n8 9
    ---###---###---###
    hj985  hj584  9b y68  94   7n8 9
    ---###---###---###
    235u4398654u9867
    g45uy 9#85498hj5#4h98yj6
    2#543y8#9675498 ghjf8#rghfd
    436u8934 89rghjre98 gynh9854
    ---###---###---###
    rehtg vnudfj rei4grh
    rtyhtr 43 mt43 m9
    435784 9hgfuh fg
    
    
    
    
    
    
    
    */
    

      

    C. Regular Bracket Sequence

    显然两种括号互不影响,故全按照$(((())))$的方式构造即可。

    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    #include<string>
    #include<ctype.h>
    #include<math.h>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    #include<bitset>
    #include<algorithm>
    #include<time.h>
    using namespace std;
    void fre() { freopen("brackets.in", "r", stdin); freopen("brackets.out", "w", stdout); }
    #define MS(x, y) memset(x, y, sizeof(x))
    #define ls o<<1
    #define rs o<<1|1
    typedef long long LL;
    typedef unsigned long long UL;
    typedef unsigned int UI;
    template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
    template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
    const int N = 0, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
    template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
    int casenum, casei;
    char s[110];
    int n, l, r;
    
    int main()
    {
        fre();
    	scanf("%d%d%d", &n, &l, &r);
        for(int i = l; i <= (l + r) / 2; i ++){
            s[i] = '[';
        }
        for(int i = (l + r) / 2 + 1; i <= r; i ++){
            s[i] = ']';
        }
        int len = (n - (r - l + 1)) / 2;
        for(int i = 1; i <= n; i ++){
            if(i < l || i > r){
                if(len) {s[i] = '('; len --;}
                else s[i] = ')';
            }
        }
        printf("%s
    ", s + 1);
    
    	return 0;
    }
    /*
    【trick&&吐槽】
    
    
    【题意】
    
    
    【分析】
    
    
    【时间复杂度&&优化】
    
    
    */
    

      

    D. Brick Counting

    旋转坐标系,转化为若$(x,y)$以及$(x,y+1)$都有砖块,则$(x+1,y)$处可以放入一个砖块。

    从下到上考虑每个$x$,那么若某个$y$右侧不存在$y+1$,则它不可以存在,用set维护即可。

    时间复杂度$O(nlog n)$。

    #include<cstdio>
    #include<algorithm>
    #include<set>
    #include<map>
    using namespace std;
    typedef long long ll;
    const int N=300010,inf=~0U>>1;
    int n,i;
    ll ans;
    int X,pos;
    set<int>T,G;
    struct P{int x,y;}a[N];
    inline bool cmp(const P&a,const P&b){return a.x<b.x;}
    int main(){
        freopen("brick-count.in","r",stdin);
        freopen("brick-count.out","w",stdout);
        scanf("%d",&n);
        for(i=1;i<=n;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            a[i].x=x;
            a[i].y=(y-x)/2;
        }
        sort(a+1,a+n+1,cmp);
        pos=1;
        X=-inf;
        //for(i=1;i<=n;i++)printf("%d %d
    ",a[i].x,a[i].y);
        while(1){
            if(T.size()==0){
                if(pos>n)break;
                X=a[pos].x;
            }
            //printf("X=%d:
    ",X);
            set<int>W;
            W.clear();
            for(set<int>::iterator it=G.begin();it!=G.end();it++){
                int x=*it;
                //printf("del %d
    ",x);
                T.erase(x);
                if(T.find(x-1)!=T.end())W.insert(x-1);
            }
            G=W;
            while(pos<=n&&a[pos].x==X){
                int x=a[pos++].y;
                if(T.find(x)!=T.end())continue;
                //printf("ins %d
    ",x);
                T.insert(x);
                if(T.find(x+1)==T.end())G.insert(x);
                if(T.find(x-1)!=T.end())G.erase(x-1);
            }
            ans+=T.size();
            X++;
        }
        printf("%lld",ans);
    }
    /*
    7
    0 0
    0 2
    2 0
    3 1
    1 3
    3 5
    0 6
    */
    

      

    E. Cross on a Plane

    留坑。

    F. Lights

    显然答案为$1$到$n$的距离的$3$倍,贪心检查是否存在可行解即可。

    #include<stdio.h>
    #include<iostream>
    #include<string.h>
    #include<string>
    #include<ctype.h>
    #include<math.h>
    #include<set>
    #include<map>
    #include<vector>
    #include<queue>
    #include<bitset>
    #include<algorithm>
    #include<time.h>
    using namespace std;
    void fre() { freopen("lights.in", "r", stdin); freopen("lights.out", "w", stdout); }
    #define MS(x, y) memset(x, y, sizeof(x))
    #define ls o<<1
    #define rs o<<1|1
    typedef long long LL;
    typedef unsigned long long UL;
    typedef unsigned int UI;
    template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
    template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
    const int N = 1e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
    template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
    int casenum, casei;
    int n;
    LL a[N], r[N];
    LL solve()
    {
        multiset<LL>sot;
        LL ans = (a[n] - a[1]) * 3;
        int pos = 1;
        LL can = a[1] + r[1];
        while(pos < n && a[pos + 1] <= can)
        {
            ++pos;
            gmax(can, a[pos] + r[pos]);
        }
        if(pos != n)return -1;
        for(int i = 1; i <= n; ++i)
        {
            sot.insert(a[i] - r[i]);
        }
    
        for(int i = 1; i < n; ++i)
        {
            sot.erase(sot.find(a[i] - r[i]));
            if(*sot.begin() > a[i])return -1;
            //printf("%lld %lld
    ", *sot.begin(), a[i]);
        }
        return ans;
    }
    int main()
    {
        fre();
    	while(~scanf("%d", &n))
    	{
            for(int i = 1; i <= n; ++i)
            {
                scanf("%lld", &a[i]);
                scanf("%lld", &r[i]);
            }
            printf("%lld
    ", solve());
        }
    
    	return 0;
    }
    /*
    【trick&&吐槽】
    
    
    【题意】
    
    
    【分析】
    我们需要找到一个延展最远的,且能覆盖到起电的点,去关掉起点
    于是——
    我们对于所有能够覆盖起点的点,选取一个右界尽可能远的。
    
    然后接下来怎么做?
    我们把左边的所有点都删掉,
    
    【时间复杂度&&优化】
    
    5
    1 5
    3 1
    4 9
    7 8
    8 4
    
    2
    1 1
    10 10
    
    2
    1 10
    10 8
    
    2
    1 1000000000
    1000000000 999999999
    
    4
    1 300
    100 100
    300 300
    600 300
    
    3
    1 100
    3 2
    6 3
    
    */
    

      

    G. Pigeonhole Principle

    留坑。

    H. Sophie’s Sets

    考虑容斥,枚举哪些集合必然满足,其它随意,枚举这些集合满足两个条件之中的哪一个,求交后用组合数求出方案数即可。

    压位存储集合,需要优秀的代码实现,配合剪枝才能通过。

    时间复杂度$O(frac{n3^m}{64})$。

    #include<cstdio>
    typedef unsigned long long ll;
    const int N=110,M=20,P=1000000007;
    int n,m,cnt,K,S,i,j,k,x,C[N][N],f[N][N],ans;ll a[M][2],b[M][2],w[2];
    void dfsadd(int x,ll A,ll B,ll C,ll D){
      if(!A&&!B||!C&&!D)return;
      if(x==cnt){
        //printf("%d %d
    ",__builtin_popcountll(A)+__builtin_popcountll(B),__builtin_popcountll(C)+__builtin_popcountll(D));
        f[__builtin_popcountll(A)+__builtin_popcountll(B)][__builtin_popcountll(C)+__builtin_popcountll(D)]++;
        return;
      }
      dfsadd(x+1,A&b[x][0],B&b[x][1],C&~b[x][0],D&~b[x][1]);
      dfsadd(x+1,A&~b[x][0],B&~b[x][1],C&b[x][0],D&b[x][1]);
    }
    void dfssub(int x,ll A,ll B,ll C,ll D){
      if(!A&&!B||!C&&!D)return;
      if(x==cnt){
        f[__builtin_popcountll(A)+__builtin_popcountll(B)][__builtin_popcountll(C)+__builtin_popcountll(D)]--;
        return;
      }
      dfssub(x+1,A&b[x][0],B&b[x][1],C&~b[x][0],D&~b[x][1]);
      dfssub(x+1,A&~b[x][0],B&~b[x][1],C&b[x][0],D&b[x][1]);
    }
    int main(){
      freopen("separating-sets.in","r",stdin);
      freopen("separating-sets.out","w",stdout);
      scanf("%d%d%d",&n,&m,&K);
      for(C[0][0]=i=1;i<=n;i++)for(C[i][0]=j=1;j<=i;j++)C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
      for(i=0;i<m;i++){
        scanf("%d",&k);
        while(k--){
          scanf("%d",&x);
          x--;
          a[i][x>>6]^=1ULL<<(x&63);
        }
        //printf("->%d %llu
    ",i,a[i][0]);
      }
      for(i=0;i<n;i++)w[i>>6]^=1ULL<<(i&63);
      for(S=1;S<1<<m;S++){
        for(cnt=i=0;i<m;i++)if(S>>i&1){
          b[cnt][0]=a[i][0];
          b[cnt][1]=a[i][1];
          cnt++;
        }
        //printf("S=%d cnt=%d
    ",S,cnt);
        //for(i=0;i<cnt;i++)printf("%llu ",b[i][0]);puts("");
        if(cnt&1)dfsadd(0,w[0],w[1],w[0],w[1]);else dfssub(0,w[0],w[1],w[0],w[1]);
      }
      for(i=K;i<=n;i++)for(j=K;j<=n;j++)ans=(1LL*C[i][K]*C[j][K]%P*f[i][j]+ans)%P;
      printf("%d",(ans+P)%P);
    }
    

      

    I. Puzzle with Tables

    留坑。

    J. Travelling to Random Cities

    因为$n=100000,m=300000$,且图随机,那么每个点平均连了$6$条边,且两点间最短路一般不会超过$8$。

    对于每个询问$S,T$,若两个点不连通,那么显然无解,可以$O(1)$判断。

    从$S$和$T$分别爆搜$4$步,那么除去最短路过来的那条边,平均还剩$5$条边,可以得到所有不超过$8$的答案,时间复杂度$O(6 imes 5^3)$。

    若此时还得不到答案,那么答案超过$8$,是小概率事件,直接$O(n+m)$BFS整张图即可。

    #include<cstdio>
    #include<algorithm>
    #include<set>
    #include<map>
    #include<cstdlib>
    #include<ctime>
    #include<vector>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int>P;
    const int N=100010,M=600010,inf=~0U>>1;
    int K=4,lim=10000;
    int n,m,Q,i,g[N],v[M],nxt[M],ed;
    int h,t,q[N],d[N];
    int ans,POS,vis[N];
    
    vector<int>e[N];
    
    int p1,p2,v1[N],v2[N],d1[N],d2[N];
    
    set<P>T;
    int cnt=0,all;
    int f[N];
    inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
    int bfs(int S,int T){
        int i;
        for(i=1;i<=n;i++)d[i]=-1;
        d[q[h=t=1]=S]=0;
        while(d[T]<0){
            int x=q[h++];
            for(i=g[x];i;i=nxt[i])if(d[v[i]]<0)d[q[++t]=v[i]]=d[x]+1;
        }
        return d[T];
    }
    inline void bfs1(int S){
        int i;
        v1[S]=++p1;
        d1[q[h=t=1]=S]=0;
        while(h<=t){
            int x=q[h++];
            if(d1[x]==K)continue;
            for(i=g[x];i;i=nxt[i])if(v1[v[i]]<p1){
                d1[q[++t]=v[i]]=d1[x]+1;
                v1[v[i]]=p1;
            }
        }
        all+=t;
    }
    inline void bfs2(int S){
        int i;
        v2[S]=++p2;
        d2[q[h=t=1]=S]=0;
        while(h<=t){
            int x=q[h++];
            if(v1[x]==p1&&d1[x]+d2[x]<ans)ans=d1[x]+d2[x];
            if(d2[x]==K)continue;
            for(i=g[x];i;i=nxt[i])if(v2[v[i]]<p1){
                d2[q[++t]=v[i]]=d2[x]+1;
                v2[v[i]]=p1;
            }
        }
        all+=t;
    }
    int F(int x){return f[x]==x?x:f[x]=F(f[x]);}
    void dfs1(int x,int y,int z){
        if(vis[x]<POS)vis[x]=POS,d[x]=y;else if(d[x]>y)d[x]=y;else return;
        if(y==K)return;
        y++;
        for(vector<int>::iterator i=e[x].begin();i!=e[x].end();i++)
        if(*i!=z)dfs1(*i,y,x);
    }
    void dfs2(int x,int y,int z){
        if(vis[x]==POS&&d[x]+y<ans)ans=d[x]+y;
        if(y==K||y>=ans)return;
        y++;
        for(vector<int>::iterator i=e[x].begin();i!=e[x].end();i++)
        if(*i!=z)dfs2(*i,y,x);
    }
    int main(){
        freopen("travelling.in","r",stdin);
        freopen("travelling.out","w",stdout);
        srand(time(NULL));
        scanf("%d%d",&n,&m);
        for(i=1;i<=n;i++)f[i]=i;
        while(m--){
            int x,y;
            scanf("%d%d",&x,&y);
            /*while(1){
                x=rand()%n+1;
                y=rand()%n+1;
                if(x==y)continue;
                if(x>y)swap(x,y);
                if(T.find(P(x,y))==T.end()){
                    T.insert(P(x,y));
                    break;
                }
            }*/
            if(F(x)!=F(y))f[f[x]]=f[y];
            add(x,y),add(y,x);
            e[x].push_back(y);
            e[y].push_back(x);
        }
        //puts("DONE");
        scanf("%d",&Q);
        while(Q--){
            int x,y;
            scanf("%d%d",&x,&y);
            //x=rand()%n+1,y=rand()%n+1;
            
            if(F(x)!=F(y)){puts("-1");continue;}
            
            POS++;
            cnt=0;
            //ans=~0U>>1;
            ans=inf;
            dfs1(x,0,0);
            dfs2(y,0,0);
            if(ans==inf)ans=bfs(x,y);
            all+=cnt;
            //printf("cnt=%d
    ",cnt);
            printf("%d
    ",ans);
        }
        //printf("all=%d
    ",all);
    }
    /*
    6 5
    1 2
    2 3
    1 3
    1 4
    4 5
    5
    1 3
    4 2
    3 5
    5 1
    4 6
    */
    

      

    K. Cypher

    从高位到低位考虑,设$f[i][j]$表示考虑到第$i$位,第$i$位之前部分的和还剩$j$时,最小的$a$是多少,因为当$j>n$时必然无解,故只需要考虑不超过$n$的状态。

    时间复杂度$O(nlog a)$。

    #include<cstdio>
    #include<algorithm>
    typedef long long ll;
    const ll inf=1LL<<62;
    using namespace std;
    const int N=63,M=400010;
    int n,i,j,k,c[N][2];ll x,S,f[N][M];
    inline void up(ll&a,ll b){a>b?(a=b):0;}
    int main(){
        freopen("xor-cypher.in","r",stdin);
        freopen("xor-cypher.out","w",stdout);
        scanf("%d%lld",&n,&S);
        while(n--){
            scanf("%lld",&x);
            for(i=0;i<60;i++)c[i][(x>>i&1)^1]++;
        }
        for(i=0;i<=60;i++)for(j=0;j<M;j++)f[i][j]=inf;
        f[60][0]=0;
        for(i=59;~i;i--)for(j=0;j<M;j++)if(f[i+1][j]<inf){
            for(k=0;k<2;k++){
                int t=j*2-c[i][k]+(S>>i&1);
                if(t>=0&&t<M)
                    up(f[i][t],f[i+1][j]*2+k);
            }
        }
        if(f[0][0]==inf)f[0][0]=-1;
        printf("%lld",f[0][0]);
    }
    

      

  • 相关阅读:
    任意进制间的转换
    判断线段相交 hdu 1086
    大数(高精度)加减乘除取模运算
    sqlserver2008透明书库加密
    数据库质疑
    sql2005 和sql2008 同时安装
    editrules
    sqlserver 表值函数
    sqlserver释放内存
    sql2008查看备份进度
  • 原文地址:https://www.cnblogs.com/clrs97/p/7508160.html
Copyright © 2011-2022 走看看