XVII Open Cup named after E.V. Pankratiev. GP of Two Capitals

Claris發表於2017-09-12

A. Artifact Guarding

選出的守衛需要滿足$\max(a+b)\leq \sum a$,從小到大列舉每個值作為$\max(a+b)$,在權值線段樹上找到最大的若干個$a$即可。

時間複雜度$O(n\log 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\n",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\n",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?' ':'\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\n", s + 1);

	return 0;
}
/*
【trick&&吐槽】


【題意】


【分析】


【時間複雜度&&優化】


*/

  

D. Brick Counting

旋轉座標系,轉化為若$(x,y)$以及$(x,y+1)$都有磚塊,則$(x+1,y)$處可以放入一個磚塊。

從下到上考慮每個$x$,那麼若某個$y$右側不存在$y+1$,則它不可以存在,用set維護即可。

時間複雜度$O(n\log 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\n",a[i].x,a[i].y);
    while(1){
        if(T.size()==0){
            if(pos>n)break;
            X=a[pos].x;
        }
        //printf("X=%d:\n",X);
        set<int>W;
        W.clear();
        for(set<int>::iterator it=G.begin();it!=G.end();it++){
            int x=*it;
            //printf("del %d\n",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\n",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\n", *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\n", 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\n",__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\n",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\n",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\times 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\n",cnt);
        printf("%d\n",ans);
    }
    //printf("all=%d\n",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(n\log 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]);
}

  

相關文章