Cmd2001的毒瘤水题题解
怕不是我再不写题解这题就该成没人做也没人会的千古谜题了......
T1:
仔细分析题面,发现相同就是广义SAM上节点相同,相似就是广义SAM上为从根到某个点路径的前缀。、
直接SAM上跑从根开始,每个点下界为1的最小流即可。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<map> 6 #include<queue> 7 #define debug cout 8 using namespace std; 9 const int maxn=5e3+1e2; 10 const int inf=0x3f3f3f3f; 11 12 int in[maxn],n,ans,sum; 13 14 namespace NetworkFlow { 15 int s[maxn<<2],t[maxn<<6],nxt[maxn<<6],f[maxn<<6],dep[maxn<<2],deg[maxn<<2],cnt=1; 16 int bak[maxn<<2],bcnt; 17 int st,ed,_s,_t; 18 inline void coredge(int from,int to,int flow) { 19 t[++cnt] = to , f[cnt] = flow , 20 nxt[cnt] = s[from] , s[from] = cnt; 21 } 22 inline void singledge(int from,int to,int flow) { 23 coredge(from,to,flow) , coredge(to,from,0); 24 } 25 inline bool bfs() { 26 memset(dep,-1,sizeof(dep)) , dep[st] = 0; 27 queue<int> q; q.push(st); 28 while( q.size() ) { 29 const int pos = q.front(); q.pop(); 30 for(int at=s[pos];at;at=nxt[at]) 31 if( f[at] && !~dep[t[at]] ) { 32 dep[t[at]] = dep[pos] + 1 , q.push(t[at]); 33 } 34 } 35 return ~dep[ed]; 36 } 37 inline int dfs(int pos,int flow) { 38 if( pos == ed ) return flow; 39 int ret = 0 , now = 0; 40 for(int at=s[pos];at;at=nxt[at]) 41 if( f[at] && dep[t[at]] > dep[pos] ) { 42 now = dfs(t[at],min(flow,f[at])) , 43 ret += now , flow -= now , 44 f[at] -= now , f[at^1] += now; 45 if( !flow ) return ret; 46 } 47 if( !ret ) dep[pos] = -1; 48 return ret; 49 } 50 inline int dinic() { 51 int ret = 0 , now = 0; 52 while( bfs() ) { 53 while( ( now = dfs(st,inf) ) ) 54 ret += now; 55 } 56 return ret; 57 } 58 inline int findflow() { 59 for(int at=s[_t];at;at=nxt[at]) 60 if( t[at] == _s ) return f[at^1]; 61 } 62 inline void backup() { 63 memcpy(bak,s,sizeof(s)) , bcnt = cnt; 64 } 65 inline void restore() { 66 memcpy(s,bak,sizeof(bak)) , cnt = bcnt; 67 } 68 } 69 70 namespace SAM { 71 map<int,int> ch[maxn<<1]; 72 int fa[maxn<<1],len[maxn<<1],root,last,cnt; 73 74 inline int NewNode(int ll) { 75 len[++cnt] = ll; 76 return cnt; 77 } 78 inline void extend(int x) { 79 int p = last; 80 int np = NewNode(len[p]+1); 81 while( p && ch[p].find(x) == ch[p].end() ) ch[p][x] = np , p = fa[p]; 82 if( !p ) fa[np] = root; 83 else { 84 int q = ch[p][x]; 85 if( len[q] == len[p] + 1 ) fa[np] = q; 86 else { 87 int nq = NewNode(len[p]+1); 88 ch[nq] = ch[q] , fa[nq] = fa[q]; 89 fa[np] = fa[q] = nq; 90 while( p && ch[p][x] == q ) ch[p][x] = nq , p = fa[p]; 91 } 92 } 93 last = np; 94 } 95 inline void Ex_extend(int* sou,int li) { 96 last = root; 97 for(int i=1;i<=li;i++) { 98 if( ch[last].find(sou[i]) != ch[last].end() ) last = ch[last][sou[i]]; 99 else extend(sou[i]); 100 } 101 } 102 } 103 104 inline void build() { 105 using SAM::ch;using SAM::cnt; 106 using namespace NetworkFlow; 107 _s = cnt * 2 + 1 , _t = _s + 1 , st = _t + 1 , ed = st + 1; 108 #define cov(x) (x+cnt) 109 for(int i=1;i<=cnt;i++) { 110 if( i != 1 ) ++deg[i] , --deg[cov(i)]; 111 for(map<int,int>::iterator it=ch[i].begin();it!=ch[i].end();it++) { 112 const int tar = it->second; 113 if( i == 1 ) singledge(_s,tar,1); 114 else singledge(cov(i),tar,1); 115 } 116 if( i != 1 ) singledge(cov(i),_t,1); 117 } 118 backup(); 119 for(int i=1;i<=_t;i++) { 120 if( !deg[i] ) continue; 121 if( deg[i] > 0 ) singledge(i,ed,deg[i]) , sum += deg[i]; 122 else singledge(st,i,-deg[i]); 123 } 124 singledge(_t,_s,inf); 125 } 126 inline int getans() { 127 using namespace NetworkFlow; 128 int d = dinic(); 129 if( d != sum ) return -1; // No solution . 130 int ret = findflow(); 131 restore(); 132 st = _t , ed = _s; 133 int dd = dinic(); 134 return ret - dd; 135 } 136 137 int main() { 138 static int m; 139 SAM::root = SAM::NewNode(0); 140 scanf("%d",&m); 141 while(m--) { 142 scanf("%d",&n); 143 for(int i=1;i<=n;i++) scanf("%d",in+i); 144 SAM::Ex_extend(in,n); 145 } 146 build(); 147 ans = getans(); 148 printf("%d\n",ans); 149 return 0; 150 }
T2:
观察操作数量特点,发现可持久化块状数组可过。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int maxn=1e6+1e2,maxb=1e3,maxu=1e4+1e2,maxr=1e5+1e2; 7 8 inline int* NewArray() { 9 static const int maxu = 1e4 + 1e3 + 10; 10 static int dat[maxu][maxb],cnt; 11 return dat[cnt++]; 12 } 13 14 struct PersistentBlockedArray { 15 int* p[maxb]; 16 inline void insert(int pos,int x) { // insert into this array . 17 if( !p[pos/maxb] ) p[pos/maxb] = NewArray(); 18 p[pos/maxb][pos%maxb] = x; 19 } 20 inline void modify(int pos,int x) { 21 int* t = NewArray(); 22 memcpy(t,p[pos/maxb],sizeof(int)*maxb); 23 t[pos%maxb] = x , p[pos/maxb] = t; 24 } 25 inline int query(int pos) { 26 return p[pos/maxb][pos%maxb]; 27 } 28 }dat[maxu]; 29 30 int ptr[maxu+maxr],now,cnt; 31 32 inline void roll(int tar) { 33 ptr[++now] = ptr[tar]; 34 } 35 inline void modify(int pos,int x) { 36 dat[++cnt] = dat[ptr[now]] , ptr[now] = cnt; 37 dat[ptr[now]].modify(pos,x); 38 } 39 inline int query(int pos) { 40 return dat[ptr[now]].query(pos); 41 } 42 43 namespace IO { 44 const int BS = 1 << 21; 45 char ibuf[BS],obuf[BS],*ist,*ied,*oed=obuf; 46 inline char nextchar() { 47 if( ist == ied ) ied = ibuf + fread(ist=ibuf,1,BS,stdin); 48 return ist == ied ? -1 : *ist++; 49 } 50 inline int getint() { 51 int ret = 0 , ch; 52 while( !isdigit(ch=nextchar()) ); 53 do ret=ret*10+ch-'0'; while( isdigit(ch=nextchar()) ); 54 return ret; 55 } 56 inline void getstr(char* s) { 57 char ch; 58 while( !isalpha(ch=nextchar()) ) ; 59 do *s++=ch; while( isalpha(ch=nextchar()) ); 60 } 61 inline void flush() { 62 //cerr<<"in flush delta = "<<oed-obuf<<endl; 63 fwrite(obuf,1,oed-obuf,stdout) , oed = obuf; 64 } 65 inline void printchar(const char &x) { 66 *oed++ = x; 67 //cerr<<"delta = "<<oed-obuf<<endl; 68 if( oed == obuf + BS ) flush(); 69 } 70 inline void printint(int x) { 71 //cerr<<"x = "<<x<<endl; 72 static int stk[30],top; 73 if( !x ) printchar('0'); 74 else { 75 top = 0; 76 while(x) stk[++top] = x % 10 , x /= 10; 77 while(top) printchar('0'+stk[top--]); 78 } 79 printchar('\n'); 80 } 81 } 82 using IO::getint; 83 using IO::printint; 84 using IO::getstr; 85 using IO::flush; 86 87 int main() { 88 static int n,m,lastans,ope; 89 static char o[20]; 90 n = getint() , m = getint(); 91 for(int i=0,x;i<n;i++) x = getint() , dat[0].insert(i,x); 92 for(int i=1,p,x,t;i<=m;i++) { 93 getstr(o); 94 if( *o == 'Q' ) { 95 p = ( getint() ^ lastans ) % n; 96 printint(lastans=query(p)); 97 } else if( *o == 'M' ) { 98 ++ope , p = ( getint() ^ lastans ) % n , x = getint(); 99 modify(p,x); 100 } else if( *o == 'R' ) { 101 ++ope , t = ( getint() ^ lastans ) % ope; 102 roll(t); 103 } 104 } 105 flush(); 106 return 0; 107 }
T3:
大力反演出phi,后面的sigma(i^k)显然是k+1次多项式,拉格朗日插值即可。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define debug cout 6 #define bool unsigned char 7 typedef long long int lli; 8 using namespace std; 9 const int maxn=1e6+1e2,lim=1e6,maxk=1e3+1e1; 10 const int mod=1e9+7; 11 12 int n,k; 13 14 namespace Sieve { 15 lli sum[maxn],mem[maxn]; 16 bool vis[maxn]; 17 18 inline void pre() { 19 static int prime[maxn/10],cnt; 20 static bool vis[maxn]; 21 sum[1] = 1; 22 for(int i=2;i<=lim;i++) { 23 if( !vis[i] ) prime[++cnt] = i , sum[i] = i - 1; 24 for(int j=1;j<=cnt&&(lli)i*prime[j]<=lim;j++) { 25 const int tar = i * prime[j]; 26 vis[tar] = 1; 27 if( i % prime[j] ) sum[tar] = sum[i] * ( prime[j] - 1 ); 28 else { 29 sum[tar] = sum[i] * prime[j]; 30 break; 31 } 32 } 33 } 34 for(int i=1;i<=lim;i++) sum[i] = ( sum[i] + sum[i-1] ) % mod; 35 } 36 inline lli getphi(lli x) { 37 if( x <= lim ) return sum[x]; 38 const lli t = n / x; 39 if( vis[t] ) return mem[t]; 40 lli& ret = mem[t]; ret = x * ( x + 1 ) >> 1 , vis[t] = 1; 41 for(lli i=2,j;i<=x;i=j+1) { 42 j = x / ( x / i ); 43 ret -= ( j - i + 1 ) * getphi(x/i) % mod , ret %= mod; 44 } 45 return ret = ( ret % mod + mod ) % mod; 46 } 47 } 48 49 namespace Inter { 50 lli in[maxk],fac[maxk],facrev[maxk],pprv[maxk],ssuf[maxk],*prv=pprv+1,*suf=ssuf+1; 51 inline lli fastpow(lli base,int tim) { 52 lli ret = 1; 53 while(tim) { 54 if( tim & 1 ) ret = ret * base % mod; 55 if( tim >>= 1 ) base = base * base % mod; 56 } 57 return ret; 58 } 59 inline void init() { 60 for(int i=1;i<k;i++) in[i] = fastpow(i,k-2); 61 for(int i=1;i<k;i++) in[i] = ( in[i] + in[i-1] ) % mod; 62 } 63 inline lli getmul(int p) { 64 return p ? fac[p] * facrev[k-p-1] % mod : facrev[k-1]; 65 } 66 inline lli getval(lli x) { 67 lli ret = 0; 68 prv[-1] = 1; 69 for(int i=0;i<k;i++) prv[i] = prv[i-1] * (x-i+mod) % mod; 70 suf[k] = 1; 71 for(int i=k-1;~i;i--) suf[i] = suf[i+1] * (x-i+mod) % mod; 72 for(int i=0;i<k;i++) { 73 lli now = prv[i-1] * suf[i+1] % mod; 74 ret = ret + now * in[i] % mod * getmul(i) % mod , ret %= mod; 75 } 76 return ret; 77 } 78 inline void getinv() { 79 static lli inv[maxn]; 80 *fac = 1; 81 for(int i=1;i<=k;i++) fac[i] = fac[i-1] * i % mod; 82 inv[k] = fastpow(fac[k],mod-2); 83 for(int i=k;i;i--) inv[i-1] = inv[i] * i % mod; 84 for(int i=1;i<=k;i++) inv[i] = inv[i] * fac[i-1] % mod; 85 for(int i=1;i<=k;i++) fac[i] = fac[i-1] * inv[i] % mod; 86 facrev[0] = 1; 87 for(int i=1;i<=k;i++) facrev[i] = facrev[i-1] * ( mod - inv[i] ) % mod; 88 } 89 } 90 inline lli segphi(lli l,lli r) { 91 return ( Sieve::getphi(r) - Sieve::getphi(l-1) + mod ) % mod; 92 } 93 94 inline lli calc(lli n) { 95 lli ret = 0; 96 for(lli i=1,j;i<=n;i=j+1) { 97 j = n / ( n / i ); 98 ret += segphi(i,j) % mod * Inter::getval(n/i) % mod , ret %= mod; 99 } 100 return ret; 101 } 102 103 int main() { 104 scanf("%d%d",&n,&k) , k += 2 , Sieve::pre() , Inter::init() , Inter::getinv(); 105 printf("%lld\n",calc(n)); 106 return 0; 107 }
当然那个RYOI是什么意思?就不告诉你!