20180616小测
论连续8天考试5场是怎样的体验......
我已经,不想再考试了啊......
(所以这就是你全程弃疗的理由?)
T1:
显然这题面里给的最后那个示例是错的......
观察到75分长度不超过1e5,我们可以大力数位DP:
f[i][j][k][p]表示长为i,首位k,最大2L-Z前缀数值为k,最小2L-Z前缀数值为P的方案数。
转移的话,枚举填哪个字符看是否合法即可转移;统计答案枚举前多少位相同即可。
这样就get到了75分,当时感觉正解大概是矩阵化什么的,然后弃疗了。
正解的确是矩阵化,但不是这样做的。
(显然这种两边拼凑形式的DP没法变成矩阵啊)
我们令f[i][j][k][p]表示前i位,是否与给定串完全相同,最大2L-Z后缀数值为k,最小2L-Z后缀数值为P的方案数。
显然这个转移可以矩阵化,然后在展开字符串的时候快速幂一发就能AC啦。
(为什么这种省选难度题我都不会了啊)
考场75分代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cassert> 6 #define debug cout 7 typedef long long int lli; 8 using namespace std; 9 const int maxn=1e5+1e2,lim=1e5; 10 const int mod=998244353; 11 12 char s[maxn]; 13 int in[maxn]; 14 int n,ans; 15 16 inline int& f(int i,int j,int k,int p) { 17 static int arr[maxn][2][4][4]; 18 return arr[i][j][k+3][p+3]; 19 } 20 inline void adde(int &dst,const int &x) { 21 if( ( dst += x ) >= mod ) dst -= mod; 22 } 23 24 inline void dp() { // 0 means l , 1 means z . 25 f(0,0,0,0) = 1; 26 for(int i=0;i<lim;i++) for(int j=0;j<2;j++) for(int k=0;k<=3;k++) for(int p=-3;p<=0;p++) if(f(i,j,k,p)) { 27 if( k + 2 <= 3 ) adde(f(i+1,0,max(k+2,2),min(p+2,0)),f(i,j,k,p)); 28 if( p - 1 >= -3 ) adde(f(i+1,1,max(k-1,0),min(p-1,-1)),f(i,j,k,p)); 29 } 30 } 31 32 inline void getans() { 33 int mx = 0 , mi = 0; ans = 1; 34 for(int i=1,rit;i<=n;i++) { // diff at bit[i] . 35 rit = n - i + 1; 36 for(int j=0;j<in[i];j++) for(int k=0;k<=3;k++) for(int p=-3;p<=0;p++) if( mx + k <= 3 && mi + p >= -3 ) adde(ans,f(rit,j,k,p)); 37 if( !in[i] ) mx = max(mx+2,2) , mi = min(mi+2,0); 38 else mx = max(mx-1,0) , mi = min(mi-1,-1); 39 } 40 } 41 42 43 inline int findint(const string &x) { 44 int ret = 0; 45 for(unsigned i=0;i<x.length();i++) { 46 if( isdigit(x[i]) ) ret = ret * 10 + x[i] - '0'; 47 else break; 48 } 49 return ret; 50 } 51 inline string cutint(const string &x) { 52 int i; 53 for(i=0;i<(signed)x.length();i++) if( !isdigit(x[i]) ) break; 54 return x.substr(i,x.length()-i); 55 } 56 inline string explain(const string &x) { 57 int i,j; 58 for(i=0;i<(signed)x.length();i++) if( x[i] == '(' ) break; 59 if( i == (signed)x.length() ) return x; 60 int su = 0; 61 for(j=0;j<(signed)x.length();j++) { 62 if( x[j] == '(' ) ++su; 63 else if( x[j] == ')' ) { 64 if( su > 1 ) --su; 65 else break; 66 } 67 } 68 string ret = x.substr(0,i) , mid = x.substr(i+1,j-i-1) , rem = x.substr(j+1,x.length()-j-1) , rit = cutint(rem); 69 int tim = findint(rem); 70 mid = explain(mid); 71 while(tim--) ret = ret + mid; 72 ret = ret + explain(rit); 73 return ret; 74 } 75 inline void explain() { 76 string ss = s; 77 string ex = explain(ss); 78 n = ex.length(); 79 for(int i=1;i<=n;i++) in[i] = ex[i-1] == 'Z'; 80 } 81 82 int main() { 83 static int T; 84 scanf("%d",&T) , dp(); 85 while(T-- ) { 86 scanf("%s",s) , explain() , getans(); 87 printf("%d\n",ans); 88 } 89 return 0; 90 }
正解代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define debug cout 6 typedef long long int lli; 7 using namespace std; 8 const int maxn=5e2+1e1,maxs=33,lim=32; 9 const int mod=998244353; 10 11 char in[maxn]; 12 13 inline int add(const int &x,const int &y) { 14 const int ret = x + y; 15 return ret >= mod ? ret - mod : ret; 16 } 17 inline int mul(const int &x,const int &y) { 18 return (lli) x * y % mod; 19 } 20 inline void adde(int &dst,const int &x) { 21 if( ( dst += x ) >= mod ) dst -= mod; 22 } 23 24 struct Matrix { 25 int dat[maxs][maxs]; 26 Matrix(int tpe=0) { memset(dat,0,sizeof(dat)); for(int i=1;i<=lim;i++) dat[i][i] = tpe; } 27 int* operator [] (const int &x) { return dat[x]; } 28 const int * operator [] (const int &x) const { return dat[x]; } 29 friend Matrix operator + (const Matrix &a,const Matrix &b) { 30 Matrix ret; 31 for(int i=1;i<=lim;i++) for(int j=1;j<=lim;j++) ret[i][j] = add(a[i][j],b[i][j]); 32 return ret; 33 } 34 friend Matrix operator * (const Matrix &a,const Matrix &b) { 35 Matrix ret; 36 for(int i=1;i<=lim;i++) for(int j=1;j<=lim;j++) for(int k=1;k<=lim;k++) adde(ret[i][j],mul(a[i][k],b[k][j])); 37 return ret; 38 } 39 inline void print() const { 40 for(int i=1;i<=32;i++) { 41 for(int j=1;j<=32;j++) debug<<dat[i][j]<<" "; 42 debug<<endl; 43 } 44 } 45 }trans[2],ini; 46 47 inline int cov(int issame,int mx,int mi) { // c in range[0,1] , mx in range[-3,0] , mi in range[0,3] . 48 return issame * 16 + mx * 4 + mi + 4; 49 } 50 inline void buildtrs() { 51 for(int j=0;j<2;j++) for(int k=0;k<=3;k++) for(int p=-3;p<=0;p++) { 52 if( k + 2 <= 3 ) { 53 trans[0][cov(1,k,p)][cov(1,max(k+2,2),min(p+2,0))] = 1; 54 trans[0][cov(0,k,p)][cov(0,max(k+2,2),min(p+2,0))] = 1; 55 trans[1][cov(1,k,p)][cov(0,max(k+2,2),min(p+2,0))] = 1; 56 trans[1][cov(0,k,p)][cov(0,max(k+2,2),min(p+2,0))] = 1; 57 } 58 if( p - 1 >= -3 ) { 59 trans[0][cov(0,k,p)][cov(0,max(k-1,0),min(p-1,-1))] = 1; 60 trans[1][cov(1,k,p)][cov(1,max(k-1,0),min(p-1,-1))] = 1; 61 trans[1][cov(0,k,p)][cov(0,max(k-1,0),min(p-1,-1))] = 1; 62 } 63 } 64 ini[1][cov(1,0,0)] = 1; 65 } 66 67 inline Matrix fastpow(Matrix base,int tim) { 68 Matrix ret(1); 69 while(tim) { 70 if( tim & 1 ) ret = ret * base; 71 if( tim >>= 1 ) base = base * base; 72 } 73 return ret; 74 } 75 inline Matrix getseg(const string &x) { 76 Matrix ret(1); 77 for(int i=0;i<(signed)x.size();i++) ret = ret * trans[x[i]=='Z']; 78 return ret; 79 } 80 inline int findint(const string &x) { 81 int ret = 0; 82 for(unsigned i=0;i<x.length();i++) { 83 if( isdigit(x[i]) ) ret = ret * 10 + x[i] - '0'; 84 else break; 85 } 86 return ret; 87 } 88 89 inline string cutint(const string &x) { 90 int i; 91 for(i=0;i<(signed)x.length();i++) if( !isdigit(x[i]) ) break; 92 return x.substr(i,x.length()-i); 93 } 94 inline Matrix explain(const string &x) { 95 int i,j; 96 for(i=0;i<(signed)x.length();i++) if( x[i] == '(' ) break; 97 if( i == (signed)x.length() ) return getseg(x); 98 int su = 0; 99 for(j=0;j<(signed)x.length();j++) { 100 if( x[j] == '(' ) ++su; 101 else if( x[j] == ')' ) { 102 if( su > 1 ) --su; 103 else break; 104 } 105 } 106 string lft = x.substr(0,i) , mid = x.substr(i+1,j-i-1) , rem = x.substr(j+1,x.length()-j-1) , rit = cutint(rem); 107 Matrix ret = getseg(lft) * fastpow(explain(mid),findint(rem)) * explain(rit); 108 return ret; 109 } 110 inline int solve(const string &x) { 111 Matrix ans = ini * explain(x); 112 int ret = 0; 113 for(int i=1;i<=lim;i++) adde(ret,ans[1][i]); 114 return ret; 115 } 116 117 int main() { 118 static int T; 119 scanf("%d",&T) , buildtrs(); 120 while(T--) scanf("%s",in+1) , printf("%d\n",solve((string)(in+1))); 121 return 0; 122 }
T2:
看到这么多SubTask......
首先答案就是排好序后的偶数项和减奇数项和。另外答案可以直接用unsigned long long存,运算过程中即使爆掉了也没有关系,反正最终答案在范围内即可。
SubTask12,直接枚举起始点用非旋转treap维护排好序的序列即可;SubTask3,可以统计每个数字在奇数位或者偶数位的共线,手玩一下就好了;SubTask4,只有存在奇数个1的区间对答案贡献1,手玩一下也就好了。
好的,你get到了60分,想不到正解,可以弃疗了。
正解的确是统计贡献,但是不是统计单点贡献,而是统计区间贡献。
考虑我们把点值排好序后的每个区间,会被计入答案多少次。我们将<=这个区间左端点的数值变成0,>=这个区间右端点的数值变成1,那么,这个区间被计入答案的可选原序列区间一定包含奇数个1。
然后线段树维护一下就行了,区间取反,统计01,线段树的基本操作。
(为什么这种NOIP数据结构题我也不会了啊)
考场60分代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cassert> 6 #define debug cout 7 typedef unsigned long long int ulli; 8 using namespace std; 9 const int maxn=3e5+1e2; 10 11 int in[maxn],n; 12 ulli ans; // ans will be correct moding 2 ^ 64 . 13 14 namespace Force { 15 const int maxn=2e3+1e2; 16 struct pii {int l,r;}; 17 struct RotatelessTreap { 18 int ch[maxn][2],siz[maxn],fix[maxn]; 19 ulli dat[maxn],su[maxn][2]; // 0 means even , 1 means odd . 20 RotatelessTreap() { 21 for(int i=0;i<maxn;i++) fix[i] = i; 22 random_shuffle(fix,fix+maxn); 23 } 24 inline void maintain(int pos) { 25 siz[pos] = siz[ch[pos][0]] + siz[ch[pos][1]] + 1; 26 if( ! ( siz[ch[pos][0]] & 1 ) ) { 27 su[pos][0] = su[ch[pos][0]][0] + su[ch[pos][1]][1]; 28 su[pos][1] = su[ch[pos][0]][1] + dat[pos] + su[ch[pos][1]][0]; 29 } else { 30 su[pos][0] = su[ch[pos][0]][0] + dat[pos] + su[ch[pos][1]][0]; 31 su[pos][1] = su[ch[pos][0]][1] + su[ch[pos][1]][1]; 32 } 33 } 34 inline pii split(int pos,ulli nv) { // left is <= nv . 35 if( !pos ) return (pii){0,0}; 36 if( dat[pos] > nv ) { 37 pii spl = split(ch[pos][0],nv); 38 ch[pos][0] = spl.r , maintain(pos); 39 return (pii){spl.l,pos}; 40 } else { 41 pii spr = split(ch[pos][1],nv); 42 ch[pos][1] = spr.l , maintain(pos); 43 return (pii){pos,spr.r}; 44 } 45 } 46 inline int merge(int x,int y) { // assert dat[x] <= dat[y] . 47 if( !x || !y ) return x | y; 48 assert(dat[x]<=dat[y]); 49 if( fix[x] > fix[y] ) { 50 ch[x][1] = merge(ch[x][1],y) , maintain(x); 51 return x; 52 } else { 53 ch[y][0] = merge(x,ch[y][0]) , maintain(y); 54 return y; 55 } 56 } 57 inline void reset(int x,ulli dd) { 58 su[x][0] = ch[x][0] = ch[x][1] = 0 , su[x][1] = dat[x] = dd , siz[x] = 1; 59 } 60 inline void insert(int &root,int x) { 61 pii sp = split(root,dat[x]); 62 root = merge(sp.l,merge(x,sp.r)); 63 } 64 inline ulli query(int root) { 65 return su[root][0] - su[root][1]; 66 } 67 }treap; 68 69 inline void getans() { 70 for(int i=1,root;i<=n;i++) { 71 treap.reset(root=i,in[i]); 72 for(int j=i+1;j<=n;j++) { 73 treap.reset(j,in[j]) , treap.insert(root,j); 74 if( ( j - i ) & 1 ) ans += treap.query(root); 75 } 76 } 77 } 78 } 79 80 namespace Order { 81 inline ulli calc_odd(int i) { 82 int before = i , after = n - i; 83 return (ulli) ( ( before + 1 ) >> 1 ) * (ulli) ( ( after + 1 ) >> 1 ); 84 } 85 inline ulli calc_even(int i) { 86 int before = i , after = n - i; 87 return (ulli) ( before >> 1 ) * (ulli) ( ( after >> 1 ) + 1 ); 88 } 89 inline void getans() { 90 for(int i=1;i<=n;i++) ans += calc_even(i) * in[i] , ans -= calc_odd(i) * in[i]; 91 } 92 } 93 94 namespace Less { 95 int su[maxn],prf[2][2]; 96 inline void getans() { 97 for(int i=1;i<=n;i++) su[i] = su[i-1] + in[i]; 98 prf[0][0] = 1; 99 for(int i=1;i<=n;i++) ans += prf[i&1][(su[i]&1)^1] , ++prf[i&1][su[i]&1]; 100 } 101 } 102 103 int main() { 104 static bool order = 1 , less = 1; 105 scanf("%d",&n); 106 for(int i=1;i<=n;i++) { 107 scanf("%d",in+i); 108 if( i != 1 && in[i] < in[i-1] ) order = 0; 109 if( in[i] > 1 ) less = 0; 110 } 111 if( n <= 2000 ) Force::getans(); 112 else if( order ) Order::getans(); 113 else if( less ) Less::getans(); 114 else srand((unsigned long long)new char) , ans = rand() * rand(); 115 printf("%llu\n",ans); 116 return 0; 117 }
正解代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 #define debug cout 7 typedef unsigned long long int ulli; 8 using namespace std; 9 const int maxn=3e5+1e2; 10 11 struct SegmentTree { 12 ulli su[maxn<<2][2]; 13 bool lazy[maxn<<2]; 14 #define lson(pos) (pos<<1) 15 #define rson(pos) (pos<<1|1) 16 inline void maintain(int pos) { 17 for(int i=0;i<2;i++) su[pos][i] = su[lson(pos)][i] + su[rson(pos)][i]; 18 } 19 inline void apply(int pos) { 20 swap(su[pos][0],su[pos][1]) , lazy[pos] ^= 1; 21 } 22 inline void push(int pos) { 23 if( lazy[pos] ) apply(lson(pos)) , apply(rson(pos)) , lazy[pos] ^= 1; 24 } 25 inline void build(int pos,int l,int r,const int &fix) { // fix will be 1 if build even . 26 if( l == r ) return void( su[pos][0] = ( l & 1 ) ^ fix ); 27 const int mid = ( l + r ) >> 1; 28 build(lson(pos),l,mid,fix) , build(rson(pos),mid+1,r,fix) , maintain(pos); 29 } 30 inline void update(int pos,int l,int r,const int &ll,const int &rr) { 31 if( ll <= l && r <= rr ) return apply(pos); 32 const int mid = ( l + r ) >> 1; push(pos); 33 if( ll <= mid ) update(lson(pos),l,mid,ll,rr); 34 if( mid < rr ) update(rson(pos),mid+1,r,ll,rr); 35 maintain(pos); 36 } 37 inline ulli query() { 38 return su[1][0] * su[1][1]; 39 } 40 }sgt[2]; 41 42 int in[maxn],srt[maxn],len,n; 43 vector<int> app[maxn]; 44 ulli ans; 45 46 int main() { 47 scanf("%d",&n); 48 for(int i=1;i<=n;i++) scanf("%d",in+i) , srt[i] = in[i]; 49 std::sort(srt+1,srt+1+n) , len = unique(srt+1,srt+1+n) - srt - 1; 50 for(int i=1;i<=n;i++) app[in[i]=lower_bound(srt+1,srt+1+len,in[i])-srt].push_back(i); 51 for(int i=0;i<2;i++) sgt[i].build(1,0,n,i^1); 52 for(int i=len;i>1;i--) { 53 for(unsigned j=0;j<app[i].size();j++) for(int k=0;k<2;k++) sgt[k].update(1,0,n,app[i][j],n); 54 for(int j=0;j<2;j++) ans += ( srt[i] - srt[i-1] ) * sgt[j].query(); 55 } 56 printf("%llu\n",ans); 57 return 0; 58 }
T3:
看到这题第一想法:谁先取模再gcd谁sb。
感觉是杜教筛筛phi之类的东西,然而并推不出可行的计算式......然后就弃疗爆零啦!
正解是这样的:
又因为:
所以我们的上标就能辗转相除啦:
然后我们分离AB单独计算:
(左边的X^d可以等比数列求和,右边gcd的那个变换,不懂的话,可以退役了)
于是杜教筛一发phi就好了。
(为什么这种莫比乌斯反演板子题都不会了啊,我这数论怕是都白学了)
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<unordered_map> 6 #define debug cout 7 typedef long long int lli; 8 using namespace std; 9 const int maxn=5e7+1e2,lim=5e7; 10 const int mod=998244353; 11 12 namespace Sieve { 13 lli phi[maxn]; 14 inline void sieve() { 15 static int prime[maxn/10],cnt; 16 static bool vis[maxn]; 17 phi[1] = 1; 18 for(int i=2;i<=lim;i++) { 19 if( !vis[i] ) prime[++cnt] = i , phi[i] = i - 1; 20 for(int j=1;j<=cnt&&(lli)i*prime[j]<=lim;j++) { 21 const int t = i * prime[j]; 22 vis[t] = 1; 23 if( i % prime[j] ) phi[t] = phi[i] * ( prime[j] - 1 ); 24 else { phi[t] = phi[i] * prime[j]; break; } 25 } 26 } 27 for(int i=1;i<=lim;i++) phi[i] = ( phi[i] + phi[i-1] ) % mod; 28 } 29 unordered_map<lli,lli> mp; 30 inline lli calc(lli x) { 31 if( x <= lim ) return phi[x]; 32 if( mp.find(x) != mp.end() ) return mp[x]; 33 lli ret = ( ( x % mod ) * ( x % mod + 1 ) >> 1 ) % mod; 34 for(lli i=2,j;i<=x;i=j+1) { 35 j = x / ( x / i ); 36 ret -= ( j - i + 1 ) % mod * calc( x / i ) % mod , ret %= mod; 37 } 38 return mp[x] = ( ret + mod ) % mod; 39 } 40 } 41 42 inline lli fastpow(lli base,lli tim) { 43 lli ret = 1; 44 while(tim) { 45 if( tim & 1 ) ret = ret * base % mod; 46 if( tim >>= 1 ) base = base * base % mod; 47 } 48 return ret; 49 } 50 inline lli supow(lli x,lli inv,lli n) { 51 if( x == 1 ) return n; 52 lli t = ( fastpow(x,n+1) - 1 + mod ) % mod; 53 return t * inv % mod; 54 } 55 inline lli calc(lli x,lli n) { 56 const lli inv = fastpow(x-1,mod-2); 57 lli ret = 0; 58 for(lli i=1,j;i<=n;i=j+1) { 59 j = n / ( n / i ); 60 ret += ( supow(x,inv,j) - supow(x,inv,i-1) + mod ) % mod * ( ( 2 * Sieve::calc(n/i) % mod - 1 + mod ) % mod ) % mod , ret %= mod; 61 } 62 return ret; 63 } 64 65 66 int main() { 67 static int T; 68 static lli n,a,b; 69 scanf("%d",&T) , Sieve::sieve(); 70 while(T--) scanf("%lld%lld%lld",&n,&a,&b) , printf("%lld\n",(calc(a,n)-calc(b,n)+mod)%mod); 71 return 0; 72 }
そう 理想 望むだけ きっと
那样的 理想 只是希望着的话 一定
もう 死のう 思うだけ きっと
只是 死掉算了 这样想着的话 一定
明日になれば ほら
如果到了明天的话 你看
きっと もう 忘れて
一定 已经 忘掉了