20180507小测

对我而言相当失败的一次考试呢。
(昨天失了智一晚上没睡觉还好意思说)

T1:


看到数据范围与值域有关,是不是想到了一些有趣的事情呢?
两两gcd为1,显然我们把同时选出的数分解质因子后,每个质数出现最多一次。是不是能状压DP呢?
(然后我发现2000内的质数有将近200个,没法状压,然后写了30分暴力)
这里有一个很显然的性质,数字x>=sqrt(x)的质因子最多只有1个,所以我们只需要状压<=sqrt(2000)的质因数,即{2-43},只有14个。
其余的,我们能按照大质因子分组,然后每组最多选1个数字。
我们令f[i][sta]表示前i组,质因数状态为sta的方案数。转移的话可以枚举这组选择哪个数字,然后进行转移。
注意没有>43质因子的数字要各自一组,因为它们不存在互斥状态。
代码:

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<vector>
 4 typedef long long int lli;
 5 const int maxn=2e3+1e2,maxs=(1<<14)+5;
 6 const int prime[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43},pl=14,full=1<<14;
 7 const int mod=1e9+7;
 8 
 9 struct Node {
10     int sta,mx;
11     friend bool operator < (const Node &a,const Node &b) { return a.mx > b.mx; }
12 }ns[maxn];
13 
14 std::vector<Node> grp[maxn];
15 lli f[maxn][maxs],ans;
16 int n,cnt;
17 
18 inline Node cut(int x) {
19     int ret = 0;
20     for(int i=0;i<pl;i++)
21         if( ! ( x % prime[i] ) ) {
22             ret |= (1<<i);
23             while( ! ( x % prime[i] ) ) x /= prime[i];
24         }
25     return (Node){ret,x};
26 }
27 
28 inline void getgrp() {
29     std::sort(ns+1,ns+1+n);
30     for(int i=1;i<=n;i++) {
31         if( ns[i].mx != ns[i-1].mx || ns[i].mx == 1 ) ++cnt;
32         grp[cnt].push_back(ns[i]);
33     }
34 }
35 inline void dp() {
36     **f = 1;
37     for(int i=1;i<=cnt;i++) {
38         for(unsigned cur=0;cur<grp[i].size();cur++)
39             for(int sta=0;sta<full;sta++) {
40                 if( ! ( sta & grp[i][cur].sta ) )
41                     f[i][sta|grp[i][cur].sta] = ( f[i][sta|grp[i][cur].sta] + f[i-1][sta] ) % mod;
42                 }
43         for(int sta=0;sta<full;sta++) f[i][sta] = ( f[i][sta] + f[i-1][sta] ) % mod;
44     }
45     for(int i=0;i<full;i++) ans = ( ans + f[cnt][i] ) % mod;
46 }
47 
48 int main() {
49     scanf("%d",&n);
50     for(int i=1,t;i<=n;i++) scanf("%d",&t) , ns[i] = cut(t);
51     getgrp() , dp() , printf("%lld\n",(ans-1+mod)%mod);
52     return 0;
53 }
View Code


T2:


字符串题,我只会后缀自动机......
很好,这题,后缀自动机不可做......然后我就GG了。
考虑SAM怎么做这道题,我们用可持久话线段树记录每个点的right集合,然后我们要找<=节点len的right集合中两个数的最大差......
这个东西由于存在自身对自身的贡献,所以没法启发式合并。
然后我就写了48分哈希。

考虑用后缀数组求LCP和LCS。
我们考虑枚举这个串的循环节的一半为i,然后在这个字符串中取1+k*i位置为关键点。
然后枚举两个相邻的关键点。显然如果有一个能满足条件的串存在的话,它一定是前一半过第一个关键点,后一半过第二个关键点的。
好的,我们可以求出这两个点的LCP和LCS之和,如果>=i的话,则证明存在长度>=i的满足条件的串。
复杂度为O(nlogn),注意构造后缀数组不能用SAM,会MLE.....要么DC3或者后缀平衡树,要么老老实实写倍增(然而我不会倍增)。
爆内存的SAM:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define debug cout
 6 using namespace std;
 7 const int maxn=2e6+1e2,maxl=23;
 8 
 9 char in[maxn>>1];
10 int Log[maxn>>1],li,ans;
11 
12 struct SuffixAutomatic {
13     int ch[maxn][26],tc[maxn][26],fa[maxn],len[maxn],rit[maxn],root,last,cnt;
14     int stk[maxn],sa[maxn>>1],rnk[maxn>>1],h[maxn>>1],rmq[maxn>>1][maxl],top,sal;
15     SuffixAutomatic() { last = root = cnt = 1; }
16     inline int NewNode(int ll) {
17         return len[++cnt] = ll , cnt;
18     }
19     inline void extend(int x,int r) {
20         int p = last , np = NewNode(len[p]+1); rit[np] = r;
21         while( p && !ch[p][x] ) ch[p][x] = np , p = fa[p];
22         if( !p ) fa[np] = root;
23         else {
24             int q = ch[p][x];
25             if( len[q] == len[p] + 1 ) fa[np] = q;
26             else {
27                 int nq = NewNode(len[p]+1);
28                 memcpy(ch[nq],ch[q],sizeof(ch[q])) , fa[nq] = fa[q] , fa[np] = fa[q] = nq;
29                 while( p && ch[p][x] == q ) ch[p][x] = nq , p = fa[p];
30             }
31         }
32         last = np;
33     }
34     inline void pre(int pos) {
35         for(int i=0,t;i<26;i++) if( ( t = ch[pos][i] ) && len[t] == len[pos] + 1 ) {
36             stk[++top] = i , tc[fa[t]][stk[len[t]-len[fa[t]]]] = t , pre(t) , stk[top--] = '\0';
37         }
38     }
39     inline void dfs(int pos) {
40         if( rit[pos] ) sa[++sal] = rit[pos];
41         for(int i=0;i<26;i++) if( tc[pos][i] ) dfs(tc[pos][i]);
42     }
43     inline void geth(char* in,int li) {
44         for(int i=1;i<=sal;i++) rnk[sa[i]] = i;
45         reverse(in+1,in+1+li);
46         for(int i=1,k=0;i<=sal;i++) {
47             k -= (bool)k;
48             const int p = sa[rnk[i]-1];
49             while( in[i+k] == in[p+k] ) ++k;
50             h[rnk[i]-1] = k;
51         }
52         reverse(in+1,in+1+li);
53     }
54     inline void initrmq() {
55         for(int i=1;i<sal;i++) rmq[i][0] = h[i];
56         for(int j=1;j<=Log[sal];j++) for(int i=1;i<sal;i++) rmq[i][j] = min( rmq[i][j-1] , rmq[i+(1<<(j-1))][j-1] );
57     }
58     inline int query(int l,int r) {
59         l = rnk[l] , r = rnk[r];
60         if( l > r ) swap(l,r);
61         --r;
62         int L = Log[r-l+1];
63         return min( rmq[l][L] , rmq[r-(1<<L)+1][L] );
64     }
65     inline void work(char* in,int li) {
66         for(int i=1;i<=li;i++) extend(in[i]-'a',li-i+1);
67         pre(1) , dfs(1);
68         in[li+1] = '#' , geth(in,li) , initrmq();
69     }
70 }prf,suf;
71 
72 inline int lcp(int i,int j) {
73     return prf.query(i,j);
74 }
75 inline int lcs(int i,int j) {
76     return suf.query(li-j+1,li-i+1);
77 }
78 inline void calc(int len) {
79     for(int st=1,sst,lc;st+len<=li;st+=len) {
80         sst = st + len , lc = lcp(st,sst) + lcs(st-1,sst-1);
81         if( lc >= len ) {
82             ans = max( ans , len * 2 );
83         }
84     }
85 }
86 
87 int main() {
88     scanf("%s",in+1) , li = strlen(in+1);
89     for(int i=2;i<=li;i++) Log[i] = Log[i>>1] + 1;
90     suf.work(in,li) , reverse(in+1,in+1+li) , prf.work(in,li) , reverse(in+1,in+1+li);
91     for(int i=1;i<=li;i++) calc(i);
92     printf("%d\n",ans);
93     return 0;
94 }
View Code

倍增:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define debug cout
 6 using namespace std;
 7 const int maxn=2e6+1e2,maxl=23;
 8 
 9 char in[maxn>>1];
10 int Log[maxn>>1],li,ans;
11 
12 struct SuffixArray  {
13     int sa[maxn],rnk[maxn],h[maxn],tax[maxn],tp[maxn],rmq[maxn][maxl],n,m;
14     inline void Resort() {
15         for(int i=0;i<=m;i++) tax[i] = 0;
16         for(int i=1;i<=n;i++) tax[rnk[i]]++;
17         for(int i=1;i<=m;i++) tax[i] += tax[i-1];
18         for(int i=n;i;i--) sa[tax[rnk[tp[i]]]--] = tp[i];
19     }
20     inline bool judge(int* s,int a,int b,int l) {
21         return s[a] == s[b] && s[a+l] == s[b+l];
22     }
23     inline void buildh() {
24         for(int i=1,k=0,j;i<=n;i++) {
25             k = k ? k - 1 : 0 , j = sa[rnk[i]-1];
26             while( in[i+k] == in[j+k] ) ++k;
27             h[rnk[i]-1] = k;
28         }
29     }
30     inline void get(char* in,int nn) {
31         n = nn;
32         for(int i=1;i<=n;i++) rnk[i] = in[i] , tp[i] = i;
33         m = 127 , Resort();
34         for(int w=1,p=1;p<n;w+=w,m=p) {
35             p = 0;
36             for(int i=n-w+1;i<=n;i++) tp[++p] = i;
37             for(int i=1;i<=n;i++) if( sa[i] > w ) tp[++p] = sa[i] - w;
38             Resort(); swap(tp,rnk); rnk[sa[1]] = p = 1;
39             for(int i=2;i<=n;i++)
40                 rnk[sa[i]] = judge(tp,sa[i],sa[i-1],w) ? p : ++p;
41         }
42         buildh() , initrmq();
43     }
44     inline void initrmq() {
45         for(int i=1;i<n;i++) rmq[i][0] = h[i];
46         for(int j=1;j<=Log[n];j++) for(int i=1;i<n;i++) rmq[i][j] = min( rmq[i][j-1] , rmq[i+(1<<(j-1))][j-1] );
47     }
48     inline int query(int l,int r) {
49         l = rnk[l] , r = rnk[r];
50         if( l > r ) swap(l,r);
51         --r;
52         int L = Log[r-l+1];
53         return min( rmq[l][L] , rmq[r-(1<<L)+1][L] );
54     }
55 }prf,suf;
56 
57 inline int lcp(int i,int j) {
58     return prf.query(i,j);
59 }
60 inline int lcs(int i,int j) {
61     return suf.query(li-j+1,li-i+1);
62 }
63 inline void calc(int len) {
64     for(int st=1,sst,lc;st+len<=li;st+=len) {
65         sst = st + len , lc = lcp(st,sst) + lcs(st-1,sst-1);
66         if( lc >= len ) {
67             ans = max( ans , len * 2 );
68         }
69     }
70 }
71 
72 int main() {
73     scanf("%s",in+1) , li = strlen(in+1);
74     for(int i=2;i<=li;i++) Log[i] = Log[i>>1] + 1;
75     prf.get(in,li) , reverse(in+1,in+1+li) , suf.get(in,li);
76     for(int i=1;i<=li;i++) calc(i);
77     printf("%d\n",ans);
78     return 0;
79 }
View Code

Update 20180509:
然而现在我还不会倍增......
不过我可以后缀平衡树求后缀数组是吧。复杂度也是nlogn。
由于这题测试点1718的特殊性,后缀平衡树的log跑不满,可以随便AC。
而测试点24也能过,只有25跑2s+......
代码:

  1 #pragma GCC optimize("Ofast")
  2 #pragma GCC optimize("no-stack-protector")
  3 #pragma GCC target("avx")
  4 #include<cstdio>
  5 #include<cstring>
  6 #include<algorithm>
  7 const int maxn=1e6+1e2,maxl=23;
  8 const double alpha=0.95;
  9 
 10 char in[maxn];
 11 int Log[maxn],li,ans;
 12 
 13 struct SuffixArray  {
 14     int sa[maxn],rnk[maxn],h[maxn],rmq[maxn][maxl],n,m,sal;
 15     
 16     int at[maxn],lson[maxn],rson[maxn],siz[maxn],sf[maxn],root,cnt;
 17     int seq[maxn],sql;
 18     int fail,failfa;
 19     double v[maxn],vfl,vfr;
 20     
 21     inline bool cmp(int x,int y) {
 22         if( !x || !y ) return !x;
 23         if( in[x] != in[y] ) return in[x] < in[y];
 24         else return v[at[x-1]] < v[at[y-1]];
 25     }
 26     inline void upgrade(int pos,double l,double r) {
 27         siz[pos] = siz[lson[pos]] + siz[rson[pos]] + 1;
 28         if( std::max( siz[lson[pos]] , siz[rson[pos]] ) > siz[pos] * alpha ) fail = pos , failfa = -1 , vfl = l , vfr = r;
 29         else if( fail == lson[pos] || fail == rson[pos] ) failfa = pos;
 30     }
 31     inline void insert(int &pos,double l,double r,const int &id) {
 32         if( !pos ) {
 33             v[at[id]=pos=++cnt]= ( l + r ) / 2.0 , siz[pos] = 1 , sf[pos] = id;
 34             return;
 35         } const double vmid = ( l + r ) / 2.0;
 36         if( cmp(sf[pos],id) ) insert(rson[pos],vmid,r,id) , upgrade(pos,l,r); // id > sf[pos] .
 37         else insert(lson[pos],l,vmid,id) , upgrade(pos,l,r);
 38     }
 39     inline int rebuild(int ll,int rr,double l,double r) {
 40         const int mid = ( ll + rr ) >> 1 , pos = seq[mid];
 41         const double vmid = ( l + r ) / 2.0; v[pos] = vmid , siz[pos] = rr - ll + 1;
 42         if( ll < mid ) lson[pos] = rebuild(ll,mid-1,l,vmid);
 43         if( mid < rr ) rson[pos] = rebuild(mid+1,rr,vmid,r);
 44         return pos;
 45     }
 46     inline void dfs(int pos) {
 47         if(lson[pos]) dfs(lson[pos]);
 48         seq[++sql] = pos;
 49         if(rson[pos]) dfs(rson[pos]);
 50         lson[pos] = rson[pos] = siz[pos] = 0;
 51     }
 52     inline void insert(const int &id) {
 53         fail = 0 , failfa = -1 , insert(root,0,1,id);
 54         if(fail) {
 55             sql = 0 , dfs(fail);
 56             if( ~failfa ) {
 57                 if( fail == lson[failfa] ) lson[failfa] = rebuild(1,sql,vfl,vfr);
 58                 else rson[failfa] = rebuild(1,sql,vfl,vfr);
 59             } else root = rebuild(1,sql,0,1);
 60         }
 61     }
 62     inline void getsa(int pos) {
 63         if(lson[pos]) getsa(lson[pos]);
 64         sa[++sal] = n - sf[pos] + 1;
 65         if(rson[pos]) getsa(rson[pos]);
 66     }
 67     
 68     inline void work(char* in,int li) {
 69         n = li , std::reverse(in+1,in+1+n) , in[n+1] = '#';
 70         insert(0);
 71         for(int i=1;i<=li;i++) insert(i);
 72         getsa(root) , std::reverse(in+1,in+1+n) , geth() , initrmq();
 73     }
 74     
 75     inline void geth() {
 76         for(int i=1;i<=n;i++) rnk[sa[i]] =i;
 77         for(int i=1,k=0,j;i<=n;i++) {
 78             k = k ? k - 1 : 0 , j = sa[rnk[i]-1];
 79             while( in[i+k] == in[j+k] ) ++k;
 80             h[rnk[i]-1] = k;
 81         }
 82     }
 83     inline void initrmq() {
 84         for(int i=1;i<n;i++) rmq[i][0] = h[i];
 85         for(int j=1;j<=Log[n];j++) for(int i=1;i+(1<<(j-1))<=n;i++) rmq[i][j] = std::min( rmq[i][j-1] , rmq[i+(1<<(j-1))][j-1] );
 86     }
 87     inline int query(int l,int r) {
 88         l = rnk[l] , r = rnk[r];
 89         if( l > r ) std::swap(l,r);
 90         --r;
 91         int L = Log[r-l+1];
 92         return std::min( rmq[l][L] , rmq[r-(1<<L)+1][L] );
 93     }
 94 }prf,suf;
 95 
 96 inline int lcp(int i,int j) {
 97     return prf.query(i,j);
 98 }
 99 inline int lcs(int i,int j) {
100     return suf.query(li-j+1,li-i+1);
101 }
102 inline void calc(int len) {
103     for(int st=1,sst,lc;st+len<=li&&!ans;st+=len) {
104         sst = st + len , lc = lcp(st,sst) + lcs(st-1,sst-1);
105         if( lc >= len ) ans = len * 2;
106     }
107 }
108 
109 int main() {
110     fread(in+1,1,maxn,stdin) , li = strlen(in+1);
111     for(int i=2;i<=li;i++) Log[i] = Log[i>>1] + 1;
112     prf.work(in,li) , std::reverse(in+1,in+1+li) , suf.work(in,li);
113     for(int i=li;i&&!ans;i--) calc(i);
114     printf("%d\n",ans);
115     return 0;
116 }
View Code

 

T3:


考试的时候根本没时间看这题了(其实是前两题不会就心态爆炸弃疗了)。
正解是一个神奇的构造:

代码:

 1 #include<cstdio>
 2 #include<algorithm>
 3 const int maxn=2e5+1e2;
 4 
 5 int a[maxn],b[maxn],sou[maxn],tar[maxn],swa[maxn][2],swb[maxn][2],n,al,bl;
 6 
 7 int main() {
 8     scanf("%d",&n);
 9     for(int i=1;i<=n;i++) scanf("%d",a+i) , sou[a[i]] = i;
10     for(int i=1;i<=n;i++) scanf("%d",b+i) , tar[b[i]] = i;
11     for(int i=1,t;i<=n;i++) if( a[i] != b[i] ) {
12         if( tar[a[i]] >= sou[b[i]] ) std::swap(a[i],a[t=sou[b[i]]]) , sou[a[t]] = t , sou[a[i]] = i , swa[++al][0] = i , swa[al][1] = t;
13         else std::swap(b[i],b[t=tar[a[i]]]) , tar[b[t]] = t , tar[b[i]] = i , swb[++bl][0] = i , swb[bl][1] = t;
14     }
15     printf("%d\n",al+bl);
16     for(int i=1;i<=al;i++) printf("%d %d\n",swa[i][0],swa[i][1]);
17     for(int i=bl;i;i--) printf("%d %d\n",swb[i][0],swb[i][1]);
18     return 0;
19 }
View Code


我は此処にて石となる 同胞を護る石となる
我愿在此成为一块石 成为保护兄弟姐妹的石头
宜災いを受けてなお 物言わぬ石に在うべし
诚然地承受灾难 一言不发的石头
この災厄に触れたとて 失うがこそ卑しけれ
以体验这灾祸为荣 以错过此为耻
心より憎むものなれど 母の手に似た慰撫に泣く
虽然你从心里憎恨我 但我会用如同母亲般的手抚慰并为你流泪

我は此処にて石となる 幾千の時刻を越えた先
我愿在此成为一块石 成为历经数千年的石头
共に手をとり生きようと その顔を焼き付けて
在那脸上烙上[一起手牵手活下去吧]
呪われた血を今日は泣く 失うがこそ悲しいけれ
今天流下被诅咒的血泪 错过了才是悲伤
愛し愛されたいと哭く 鬼の姿に人の夢
为想爱与被爱而哭 化身鬼魂现于人梦

posted @ 2018-05-07 21:49  Cmd2001  阅读(287)  评论(1编辑  收藏  举报