Noip模拟69 2021.10.5
考场拼命$yy$高精度结果没学好$for$循环痛失$50pts$,当场枯死
以后一定打对拍,要不考后会。。。
T1 石子游戏
首先要知道典型的$NIM$博弈,就是说如果所有堆石子个数的异或和为$0$则先手必输
那么这道题给出了取石子上限,那么每堆石子$\mod x+1$然后异或就可以知道谁必胜了
然后这道题就转化为如何求$\sum \limits_{i=1}^{n}\oplus a_i \mod(x+1)$。
分段考虑每一段$[k(x+1),(k+1)(x+1)]$,然后预处理一个$f$数组
$UPD 2021.10.7$然而$A$掉这道题后发现并不是跟题解说的那样简单。
关于数组$f$:
题解里面预处理的$f$数组是基于位运算的,$f[i][j]$是由第$j+1$位加一个$1$转移过来的,
然后这一段区间使用了状压的思想枚举了子集算出的和累加的贡献,把后面的式子拆开就可以发现,累加的次数刚好为$[0,2^{j}-1]$,
这还表明了一个问题,就是只有偶数段才会做贡献。别着急问偶数段是啥,先听我说
刚才说累加的次数只有$2^j$次,而$2^{j+1}=2^{j} \times 2$是累加次数的二倍,再看累加的起点终点,
不难发现,如果把$2^j$长度称为一段,那么$2^{j+1}$就是两段,分开考虑这两段。
因为累加的起点在第二段开头也就是$i+2^j$而不是$i$,所以每次加上一个$2^{j+1}$长度的区间里面都只有第二段做出了累加的贡献
所以说只有偶数段才做贡献。
关于答案计算:
理解透彻$f$的转移方式之后便可以计算答案了。
对于枚举每一个$x+1$,我们枚举一个$j$,显然只会枚举到$log_2(x)$,然后我们再枚举一下可能的$k$
找到一段要计算的区间$[l,r]$,使用向下取整的方式找到距离$r$最近的$l+2^{j+1}$,这一段的贡献可以用预处理出的数组直接做后缀和求出
然后考虑区间$[l+2^{j+1},r]$,这段区间如果是在奇数区间,则不用计算单独的贡献,因为他没有,
如果有一些在偶数区间或者被偶数区间包含,就需要使用前缀和求出贡献,很好求,直接最右端减去最左端就行
算出的这个数如果是奇数,就会有贡献,给计数器上的$j$位异或一个$1$,因为要计算总和,现在枚举的是区间
需要找的是所有可能区间的答案的和是否为奇数,所以是异或,不是与,如果两个区间都是奇数那答案就会变成偶数。
这样就差不多了。
1 #include<bits/stdc++.h> 2 #define int long long 3 #define pw(x) (1<<(x)) 4 #define lg(x) (log2(x)) 5 using namespace std; 6 namespace AE86{ 7 #define out(x) cout<<#x<<":"<<x<<endl 8 #define fuck cout<<"fuck"<<endl 9 inline int read(){ 10 int x=0,f=1;char ch=getchar(); 11 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 13 }inline void write(int x,char opt='\n'){ 14 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 15 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 16 for(int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 17 }using namespace AE86; 18 const int NN=7e5+5; 19 int n,c[NN],f[NN][65]; 20 namespace WSN{ 21 inline short main(){ 22 freopen("stone.in","r",stdin); 23 freopen("stone.out","w",stdout); 24 n=read(); 25 for(int i=1;i<=n;i++) ++c[read()]; 26 for(int i=1;i<=n;i++) c[i]+=c[i-1]; 27 for(int i=n;~i;i--) 28 for(int j=0;j<=lg(n-i+1);j++) 29 f[i][j]=f[min(i+pw(j+1),n)][j]+c[min(i+pw(j+1)-1,n)]-c[i+pw(j)-1]; 30 for(int y=2;y<=n+1;y++){ 31 int tmp=0; bool flag=0; 32 for(int j=0;j<=lg(y-1);j++){ 33 for(int k=0;k*y<=n;k++){ 34 int l=k*y,r=min((k+1)*y-1,n),floor=(r-l+1)>>(j+1); bool lim=(l+floor*pw(j+1)+pw(j)<=r); 35 if((f[l][j]-f[l+floor*pw(j+1)][j]+lim*(c[r]-c[l+floor*pw(j+1)+pw(j)-1]))&1) tmp^=1<<j; 36 } 37 if(tmp){flag=1;break;} 38 } 39 printf(flag?"Alice ":"Bob "); 40 } 41 return 0; 42 } 43 } 44 signed main(){return WSN::main();}
T2 大鱼吃小鱼
直接$multiset$水四十
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 namespace AE86{ 5 inline int read(){ 6 int x=0,f=1;char ch=getchar(); 7 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 8 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 9 }inline void write(int x,char opt='\n'){ 10 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 11 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 12 for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 13 }using namespace AE86; 14 const int NN=3e5+5; 15 int n,w[NN],q,stk[NN],top; 16 multiset<int> s; 17 #define sit multiset<int>::iterator 18 namespace WSN{ 19 inline short main(){ 20 // freopen("in.in","r",stdin); freopen("sb.out","w",stdout); 21 freopen("fish.in","r",stdin); 22 freopen("fish.out","w",stdout); 23 n=read();for(int i=1;i<=n;i++) w[i]=read(),s.insert(w[i]); 24 q=read();int tim=0; 25 while(q--){ 26 int opt=read();++tim; 27 if(opt==1){ 28 int st=read(),k=read(),ans=0,flag=0; 29 if(st>=k){write(0);continue;} 30 top=0; 31 while(s.size()){ 32 sit it=s.upper_bound(st-1); 33 if(it!=s.begin()) --it; 34 else {flag=1;break;} 35 st+=*it; s.erase(it); stk[++top]=*it; 36 ++ans; 37 if(st>=k) break; 38 } 39 while(top) s.insert(stk[top--]); 40 if(flag||st<k){puts("-1");continue;} 41 write(ans); 42 } 43 if(opt==2){ 44 int v=read();s.insert(v); 45 } 46 if(opt==3){ 47 int v=read();s.erase(s.find(v)); 48 } 49 } 50 return 0; 51 } 52 } 53 signed main(){return WSN::main();}
T3 黑客
考场上没写出来的大水数论题,就是五分钟打了暴力就跑,没考虑正解(暴力分太高)
枚举分数算倍数个数就行
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 namespace AE86{ 5 inline int read(){ 6 int x=0,f=1;char ch=getchar(); 7 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 8 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 9 }inline void write(int x,char opt='\n'){ 10 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 11 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 12 for(register int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 13 }using namespace AE86; 14 const int mod=1e9+7; 15 int A,B,C,D,ans; 16 inline int gcd(int a,int b){return b?gcd(b,a%b):a;} 17 inline int mo(int x){return x>=mod?x-mod:x;} 18 namespace WSN{ 19 inline short main(){ 20 freopen("hacker.in","r",stdin); 21 freopen("hacker.out","w",stdout); 22 A=read();B=read();C=read();D=read(); 23 for(int i=1;i<=999;i++) 24 for(int j=1;j<=999-i;j++) if(gcd(i,j)==1){ 25 int l1=ceil(1.0*A/i),r1=B/i; 26 int l2=ceil(1.0*C/j),r2=D/j; 27 int a=min(r1,r2),b=max(l1,l2); 28 if(a-b+1>0) ans=mo(ans+(i+j)*((a-b+1)%mod)%mod); 29 } write(ans); 30 return 0; 31 } 32 } 33 signed main(){return WSN::main();}
T4 黑客(续)
珍爱生命,远离高精
$m=0$加暴力可以获得$70pts$可是考场上没读明白那个限制条件就只打了$m=0$还打挂了
就爆零了,非常难受,以后还事能重载运算符就重载吧,要不一遍一遍的打太容易错
就是一个普通的数位$dp$套高精度,压位高精度到$17$位刚刚好,如果特判$m=0$会更快
$f[pos][sta]$表示$dp$到前$pos$位数字集合选择状态为$sta$时的方案/总和数。
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 namespace AE86{ 5 inline int read(){ 6 int x=0,f=1;char ch=getchar(); 7 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 8 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f; 9 }inline void write(int x,char opt='\n'){ 10 char ch[20];int len=0;if(x<0)x=~x+1,putchar('-'); 11 do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); 12 for(int i=len-1;i>=0;--i)putchar(ch[i]);putchar(opt);} 13 }using namespace AE86; 14 const int NN=1005; 15 int n,m,k,ban[10]; 16 namespace TASK{ 17 int a[505],b[505],ad[505],c[5],bin[5][1005],d[1005],e[1005]; 18 inline void get(int *a,int x){for(;x;x/=10) a[++a[0]]=x%10;} 19 inline void task1(){ 20 a[0]=a[1]=1; 21 for(int T=1;T<=n;T++){ 22 memset(ad,0,sizeof(ad)); 23 for(int i=1;i<=a[0];i++){ 24 int tmp=k*a[i]; 25 a[i]=tmp%10; ad[i+1]=tmp/10; 26 if(ad[i]) a[i]+=ad[i]; 27 if(a[i]>=10) ad[i+1]+=a[i]/10,a[i]%=10; 28 } if(ad[a[0]+1]) a[0]++,a[a[0]]=ad[a[0]]; 29 if(T==n-1) memcpy(b,a,sizeof(a)); 30 } 31 for(int i=a[0];i;--i) printf("%lld",a[i]);puts(""); 32 if(n==1){cout<<k<<endl;return;} 33 int oo=(1+k)*k/2; get(c,oo); 34 for(int i=1;i<=c[0];i++){ 35 memset(ad,0,sizeof(ad)); 36 for(int j=1;j<=b[0];j++){ 37 int tmp=c[i]*b[j]; 38 bin[i][j]=tmp%10; ad[j+1]=tmp/10; 39 if(ad[j]) bin[i][j]+=ad[j]; 40 if(bin[i][j]>=10) ad[j+1]+=bin[i][j]/10,bin[i][j]%=10; 41 } 42 bin[i][0]=b[0]; 43 while(ad[b[0]+1]){ 44 bin[i][++bin[i][0]]=ad[b[0]+1]%10; 45 ad[b[0]+1]/=10; 46 } 47 if(i!=1){ 48 bin[i][0]+=(i-1); 49 for(int j=bin[i][0];j>=i-1;j--) bin[i][j]=bin[i][j-i+1]; 50 for(int j=1;j<=i-1;j++) bin[i][j]=0; 51 } 52 } 53 int maxn=0; memset(ad,0,sizeof(ad)); 54 for(int i=1;i<=c[0];i++) maxn=max(maxn,bin[i][0]); 55 for(int i=1;i<=maxn;i++){ 56 int tmp=0; 57 for(int j=1;j<=c[0];j++) tmp+=bin[j][i]; 58 ad[i+1]=tmp/10; d[i]=tmp%10; 59 if(ad[i]) d[i]+=ad[i]; 60 if(d[i]>=10) ad[i+1]+=d[i]/10,d[i]%=10; 61 } d[0]=maxn; 62 while(ad[maxn+1]){ 63 d[++d[0]]=ad[maxn+1]%10; 64 ad[maxn+1]/=10; 65 } 66 memcpy(e,d,sizeof(d)); 67 for(int i=1;i<n;i++){ 68 memset(ad,0,sizeof(ad)); 69 e[0]++; 70 for(int j=e[0];j>1;j--) swap(e[j],e[j-1]); 71 for(int j=1;j<=e[0];j++){ 72 int tmp=d[j]+e[j]; 73 ad[j+1]=tmp/10,d[j]=tmp%10; 74 if(ad[j]) d[j]+=ad[j]; 75 if(d[j]>=10) ad[j+1]+=d[j]/10,d[j]%=10; 76 } if(ad[(d[0]=e[0])+1]) d[0]++,d[d[0]]=ad[d[d[0]]]; 77 } 78 for(int i=d[0];i;i--) printf("%lld",d[i]);puts(""); 79 } 80 }using namespace TASK; 81 namespace Solve{ 82 const int base=1e16; 83 struct Big_Int{ 84 int p[65]; 85 Big_Int(){} 86 Big_Int(int x){memset(p,0,sizeof(p));p[0]=(x>0);p[1]=x;} 87 inline void print(){ 88 printf("%lld",p[p[0]]); 89 for(int i=p[0]-1;i>0;--i) printf("%016lld",p[i]); 90 puts(""); 91 } 92 Big_Int operator*(const int&x){ 93 Big_Int ret; memset(ret.p,0,sizeof(ret.p)); int add=0; ret.p[0]=p[0]; 94 for(int i=1;i<=ret.p[0];++i){ 95 ret.p[i]=p[i]*x+add; 96 add=ret.p[i]/base; 97 ret.p[i]-=add*base; 98 } 99 if(add) ret.p[++ret.p[0]]=add; 100 return ret; 101 } 102 Big_Int operator+(const Big_Int&a){ 103 Big_Int ret; memset(ret.p,0,sizeof(ret.p)); int add=0; ret.p[0]=max(a.p[0],p[0]); 104 for(int i=1;i<=ret.p[0];++i){ 105 ret.p[i]=a.p[i]+p[i]+add; 106 add=ret.p[i]/base; 107 if(ret.p[i]>=base) ret.p[i]-=base; 108 } 109 if(add) ret.p[++ret.p[0]]=add; 110 return ret; 111 } 112 }; 113 pair<Big_Int,Big_Int> f[505][1<<9]; 114 pair<Big_Int,Big_Int> dfs(int pos,int sta){ 115 if(f[pos][sta].first.p[1]>-1) return f[pos][sta]; 116 if(pos>n) return f[pos][sta]=make_pair(Big_Int(1),Big_Int(0)); 117 f[pos][sta].first.p[1]=0; 118 for(int i=1;i<=k;i++) if(!(sta&(ban[i]))){ 119 pair<Big_Int,Big_Int> res=dfs(pos+1,sta|(1<<i-1)); 120 f[pos][sta].first=f[pos][sta].first+res.first; 121 f[pos][sta].second=f[pos][sta].second+(res.first*i)+(res.second*10); 122 } 123 return f[pos][sta]; 124 } 125 }using namespace Solve; 126 namespace WSN{ 127 inline short main(){ 128 freopen("hacker2.in","r",stdin); 129 freopen("hacker2.out","w",stdout); 130 n=read();m=read();k=read(); 131 if(!m) return task1(),0; 132 for(int i=1,a,b;i<=m;++i) a=read(),b=read(),ban[a]|=1<<b-1; 133 for(int i=1;i<=n+1;i++) 134 for(int j=0;j<(1<<k);j++) f[i][j].first=Big_Int(-1); 135 pair<Big_Int,Big_Int> ans=dfs(1,0); 136 ans.first.print(); ans.second.print(); 137 return 0; 138 } 139 } 140 signed main(){return WSN::main();}