集训总结
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #define LL long long 6 using namespace std; 7 long long f[100][2][2]; 8 int T; 9 long long n; 10 //[0]比原来小,[1]比原来大 11 //[0]选0,[1]选1 12 int main(){ 13 //freopen("3.out","w",stdout); 14 //scanf("%d",&T); 15 scanf("%d",&T); 16 17 while(T--){ 18 scanf("%lld",&n); 19 20 memset(f,0,sizeof(f)); 21 if(n&1ll){ 22 f[0][0][0]=1ll; 23 f[0][0][1]=0ll; 24 f[0][1][0]=1ll; 25 f[0][1][1]=0ll; 26 } 27 else{ 28 f[0][0][0]=1ll; 29 f[0][0][1]=0ll; 30 f[0][1][0]=0ll; 31 f[0][1][1]=1ll; 32 } 33 LL i; 34 for(i=1ll;(1ll<<i)<=n;i++){ 35 if(!(n&(1ll<<i)) && !(n&(1ll<<(i-1ll) ) ) ){//00 36 //cout<<1; 37 f[i][0][0]=f[i-1][0][0]; 38 f[i][0][1]=f[i-1][1][0]+f[i-1][1][1]+f[i-1][0][1]; 39 f[i][1][0]=0ll; 40 f[i][1][1]=f[i-1][0][1]+f[i-1][0][0]; 41 } 42 else if(!(n&(1ll<<i)) && (n&(1ll<<(i-1ll) ) ) ){//01 43 // cout<<2; 44 f[i][0][0]=f[i-1][1][0]+f[i-1][0][1]+f[i-1][0][0]; 45 f[i][0][1]=f[i-1][1][1]; 46 f[i][1][0]=0ll; 47 f[i][1][1]=f[i-1][0][1]+f[i-1][0][0]; 48 } 49 else if((n&(1ll<<i)) && !(n&(1ll<<(i-1ll) ) ) ){//10 50 //cout<<3; 51 f[i][0][0]=f[i-1][1][0]+f[i-1][1][1]+f[i-1][0][1]+f[i-1][0][0]; 52 f[i][0][1]=0ll; 53 f[i][1][0]=f[i-1][0][0]; 54 f[i][1][1]=f[i-1][0][1]; 55 } 56 else{//11 57 //cout<<4; 58 f[i][0][0]=f[i-1][1][0]+f[i-1][1][1]+f[i-1][0][1]+f[i-1][0][0]; 59 f[i][0][1]=0ll; 60 f[i][1][0]=f[i-1][0][1]+f[i-1][0][0]; 61 f[i][1][1]=0ll; 62 } 63 //printf("less%d %d bigger%d %d\n",f[i][1][0],f[i][0][0],f[i][0][1],f[i][1][1]); 64 } 65 printf("%lld\n",f[i-1][1][0]+f[i-1][0][0]-1ll);//去掉0 66 } 67 return
体积巨大的完全背包【数学+背包】
n<=1e5,m<=1e18,1<=ai<=100
这道题显然要从ai的大小入手,考虑到ai 很小,我们对于1~100之间求出同体积的最大价值
然后证明一个引理??
引理:给定任意个整数(不一定不同),它们之中存在若干个整数的和为n的倍数。
证明:设n个整数为a1~an
S1~Sn 为前缀和
对于S0~Sn这n+1个数,至少有两个数模相同,则这两个数的差为的倍数,证毕。
设:S为性价比最高且体积最小的物品,x为最优情况下除S外的其他物品个数
定理:存在一种最优情况使得x< S的体积(a[s])。
证明:若x> a[s],则存在若干件物品的和为 a[s] 的倍数,将这些物品用第种物品替换一定不劣。
于是这件非第s种物品的和最大为100*a[s]。于是我们选择(n/a[x]-100)件第s种物品,剩下的空间做完全背包,复杂度O(n^3)。
force(but ac):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #include<ctime> 8 #define LL long long 9 using namespace std; 10 const int maxn=1e6+5; 11 const int maxit=1e2+5; 12 const LL INF=1ll<<62; 13 int cnt; 14 struct item{ 15 int w;//体积 16 int v;//价值 17 const bool operator<(const item & x)const { 18 return 1.0*v/w > 1.0*x.v/x.w; 19 } 20 }it[maxit]; 21 int n,a[maxn],b[maxn]; 22 int Maxvalue[maxit]; 23 LL f[maxn]; 24 LL m; 25 26 inline void force1(){ 27 for(int i=1;i<=n;i++)scanf("%d%d",&a[i],&b[i]); 28 for(int i=1;i<=m;i++){ 29 f[i]=-INF; 30 } 31 for(int i=1;i<=n;i++){ 32 for(int j=a[i];j<=m;j++){ 33 f[j]=max(max(f[j],f[j-1]),f[j-a[i]]+b[i]); 34 } 35 } 36 printf("%lld\n",f[m]); 37 exit(0); 38 } 39 inline void force2(){ 40 //cout<<"enter2"<<endl; 41 for(int i=1;i<=n;i++){ 42 scanf("%d%d",&a[i],&b[i]); 43 Maxvalue[ a[i] ]=max(Maxvalue[ a[i] ],b[i]);//相同体积价值最大化 44 } 45 //for(int i=1;i<=100;i++)printf("%d:%d\n",i,Maxvalue[i]); 46 for(int i=1;i<=m;i++){ 47 f[i]=-INF; 48 } 49 for(int i=1;i<=m;i++){ 50 for(int j=1;j<=100;j++){ 51 if(i>=j)f[i]=max(f[i],f[i-j]+Maxvalue[j]); 52 } 53 } 54 printf("%lld\n",f[m]); 55 exit(0); 56 } 57 inline void ac(){ 58 LL ans=0ll; 59 for(int i=1;i<=n;i++){ 60 scanf("%d%d",&a[i],&b[i]); 61 Maxvalue[ a[i] ]=max(Maxvalue[ a[i] ],b[i]);//相同体积价值最大化 62 } 63 for(int i=1;i<=maxit-5;i++){ 64 if(Maxvalue[i]){ 65 it[++cnt].w=i; 66 it[cnt].v=Maxvalue[i]; 67 } 68 } 69 70 sort(it+1,it+cnt+1);//斜率排序 71 for(int i=1;i<=100000;i++){//预处理 72 f[i]=-INF; 73 } 74 for(int i=1;i<=100000;i++){ 75 for(int j=1;j<=100;j++){ 76 if(i>=j)f[i]=max(f[i],f[i-j]+Maxvalue[j]); 77 } 78 } 79 ans=m/it[1].w*it[1].v+f[m%it[1].w]; 80 for(LL i=1;i<=100000;i++){ 81 //if(m==i)cout<<"shit"<<endl; 82 ans=max((LL)((m-i)/(it[1].w))*it[1].v+f[i+(m-i)%it[1].w],max(ans,(LL)(m/i)*f[i]+f[m%i])); 83 } 84 printf("%lld\n",ans); 85 } 86 int main(){ 87 //freopen("backpack3.in","r",stdin); 88 srand(time(0)); 89 90 scanf("%d%lld",&n,&m); 91 if(n<=1000&&m<=1000)force1(); 92 else if(m<=100000)force2(); 93 else ac(); 94 //else printf("%d",rand()); 95 return 0; 96 }
总结:
1.数据范围极大时 寻找突破口,往往会有一些小性质应该先证明出来
2.无法证明时尽量想暴力+优化,正解往往不好想
3.平时做题应该多思考思考数据范围大了该咋办
————————线段树shit题集锦————————
T1
本题极其无聊,3T强行捏成1T,差最大 显然是 前一半后一半,差最小显然是间隔着放,方案数显然是卡特兰数列
关于卡特兰数列,直接放公式了h(n)=C(2n,n)/(n+1) (n=0,1,2,...) or h(n)=c(2n,n)-c(2n,n-1)(n=0,1,2,...)
也就是2n 个数0 1,保证在任何位置都有:之前的1的个数<= 0的个数 之类的问题
维护:
1.求和数组,每次右半边的和 - 左半边的和
2.奇正偶负数组,每次询问时判断L是奇是偶在考虑正负
3.组合数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #include<cstdlib> 6 #include<cmath> 7 #define LL long long 8 using namespace std; 9 const LL P=1e9+7; 10 /* 11 数据结构??? 12 两个线段树,katalan数列 13 */ 14 15 const int maxn=2e6+5; 16 int n,m,c[maxn],opt,l,r; 17 LL val; 18 int sum[maxn<<2][2],lazy[maxn<<2]; 19 inline void update(int k){ 20 sum[k][0]=(1ll*sum[k<<1][0]+sum[k<<1|1][0])%P; 21 sum[k][1]=(1ll*sum[k<<1][1]+sum[k<<1|1][1])%P; 22 } 23 inline void change(int k,int l,int r,LL x){ 24 sum[k][0]=(1ll*sum[k][0]+x*(r-l+1))%P;//和 25 lazy[k]=(1ll*lazy[k]+x)%P; 26 27 if((r-l)&1)return;//差 28 else{ 29 sum[k][1]=(1ll*sum[k][1]+((l&1)?x:-x))%P; 30 } 31 } 32 void build(int k,int l,int r){ 33 if(l==r){ 34 //差数列 2n+1 + 2n - 35 sum[k][1]=( (l&1) ? c[l] : -c[l] ); 36 //和数列 37 sum[k][0]=c[l]; 38 39 return; 40 } 41 else{ 42 int mid=(l+r)>>1; 43 build(k<<1,l,mid); 44 build(k<<1|1,mid+1,r); 45 update(k); 46 } 47 } 48 inline void down(int k,int l,int r){ 49 if(lazy[k]){ 50 int mid=(l+r)>>1; 51 change(k<<1,l,mid,1ll*lazy[k]); 52 change(k<<1|1,mid+1,r,1ll*lazy[k]); 53 lazy[k]=0ll; 54 } 55 } 56 void modify(int k,int l,int r,int x,int y,LL xx){ 57 if(l>r){cout<<"shit";exit(0);} 58 59 if(x<=l && r<=y){ 60 change(k,l,r,xx); 61 return; 62 } 63 else{ 64 down(k,l,r); 65 66 int mid=(l+r)>>1; 67 if(x<=mid)modify(k<<1,l,mid,x,y,xx); 68 if(mid<y)modify(k<<1|1,mid+1,r,x,y,xx); 69 update(k); 70 } 71 } 72 int query(int k,int l,int r,int x,int y,int b){ 73 if(l>r){cout<<"shit";exit(0);} 74 75 if(x<=l&&r<=y){ 76 return sum[k][b]%P; 77 } 78 else{ 79 down(k,l,r); 80 81 int ans=0ll; 82 int mid=(l+r)>>1; 83 if(x<=mid)ans=(1ll*ans+query(k<<1,l,mid,x,y,b))%P; 84 if(mid<y)ans=(1ll*ans+query(k<<1|1,mid+1,r,x,y,b))%P; 85 86 return ans; 87 } 88 } 89 LL jc[maxn],njc[maxn],inv[maxn]; 90 inline void com_init(){ 91 jc[0]=njc[0]=jc[1]=njc[1]=inv[1]=1ll; 92 for(LL i=2ll;i<=(n<<1);i++){ 93 jc[i]=jc[i-1]*i%P; 94 inv[i]=(P-P/i)*inv[P%i]%P; 95 njc[i]=njc[i-1]*inv[i]%P; 96 } 97 } 98 inline LL C(int n,int m){//组合数注意边界问题 99 100 return jc[n]*njc[n-m]%P*njc[m]; 101 } 102 int main(){ 103 scanf("%d%d",&n,&m); 104 //if(m<=100)cout<<n<<" "<<m<<endl; 105 for(int i=1;i<=(n<<1);i++){ 106 scanf("%d",&c[i]); 107 //if(m<=100)cout<<c[i]<<" "; 108 } 109 110 com_init(); 111 build(1,1,n<<1); 112 113 for(int i=1;i<=m;i++){ 114 scanf("%d",&opt); 115 //if(m<=100)cout<<opt<<" "; 116 if(opt){ 117 scanf("%d%d",&l,&r); 118 //if(m<=100)cout<<l<<" "<<r<<endl; 119 int mid=(l+r)>>1; 120 int len=r-l+1; 121 122 printf("%d %d %lld\n",-query(1,1,n<<1,l,mid,0)+query(1,1,n<<1,mid+1,r,0),((l&1)?-query(1,1,n<<1,l,r,1):query(1,1,n<<1,l,r,1)),( ( C(len,len>>1)-C(len,(len>>1)-1) )%P+P)%P ); 123 } 124 else{ 125 126 scanf("%d%d%lld",&l,&r,&val); 127 //if(m<=100)cout<<l<<" "<<r<<" "<<val<<endl; 128 modify(1,1,n<<1,l,r,val); 129 } 130 } 131 return 0; 132 } 133 /* 134 10 10 135 0 2 10 18 19 27 27 35 35 45 46 53 56 59 67 70 75 93 93 100 1 136 0 5 10 0 137 0 6 11 7 138 0 6 7 -14 139 0 10 13 3 140 0 3 20 -4 141 0 9 16 4 142 1 16 17 143 1 5 6 144 0 5 14 7 145 146 41 13 5 147 1 1 1 148 1 1 1 149 */
...考试时空间炸了,so segment_tree在写的时候尽量不把废物信息(左右端点)存起来,尽量在递归里面写
T2
线段树维护乘积+逆元(注意long long,注意空间别爆了)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath> 5 #include<algorithm> 6 #include<iostream> 7 #define LL long long 8 using namespace std; 9 const int maxn=1e6+5; 10 const LL P=1e9+7; 11 LL x,y; 12 void exgcd(LL a,LL b){//ax+by=1 13 if(!b){ 14 x=1ll;y=0;return; 15 } 16 exgcd(b,a%b); 17 18 LL t=y; 19 y=x-(a/b)*y; 20 x=t; 21 } 22 inline LL get_inv(LL a){ 23 exgcd(a,P); 24 //cout<<a<<" "<<x<<endl; 25 return (x%P+P)%P; 26 } 27 int root,lson[maxn<<2],rson[maxn<<2],cnt; 28 LL pi[maxn<<2]; 29 void modify(int &k,int l,int r,int p,int x,int b){ 30 if(!k){ 31 k=++cnt; 32 pi[k]=1ll; 33 } 34 35 if(l==r){ 36 pi[k]=((b==1)?(pi[k]*x%P):(pi[k]*get_inv((LL)x)%P)) ; 37 return; 38 } 39 int mid=(l+r)>>1; 40 if(p<=mid)modify(lson[k],l,mid,p,x,b); 41 else modify(rson[k],mid+1,r,p,x,b); 42 43 if(lson[k]&&rson[k])pi[k]=pi[lson[k]]*pi[rson[k]]%P; 44 else if(lson[k])pi[k]=pi[lson[k]]; 45 else if(rson[k])pi[k]=pi[rson[k]]; 46 } 47 LL query(int k,int l,int r,int x,int y){ 48 if(!k)return 1ll; 49 if(x<=l && r<=y)return pi[k];//线段树敲错了555 50 51 int mid=(l+r)>>1; 52 LL ans=1ll; 53 if(x<=mid)ans=ans*query(lson[k],l,mid,x,y)%P; 54 if(mid<y)ans=ans*query(rson[k],mid+1,r,x,y)%P;//线段树写反了 55 return ans; 56 } 57 int n,m; 58 int main(){ 59 //freopen("1.in","r",stdin); 60 61 scanf("%d%d",&n,&m); 62 int x,y,z; 63 cnt=1;//之前改过了以为之后也改了 64 pi[0]=pi[1]=1ll; 65 for(int i=1;i<=m;i++){ 66 scanf("%d%d%d",&x,&y,&z); 67 if(x==3){ 68 root=1; 69 printf("%lld\n",query(root,1,n,y,z)); 70 } 71 else{ 72 root=1; 73 modify(root,1,n,y,z,x); 74 } 75 } 76 return 0; 77 }
T3
线段树+状压:
每次正常修改,用int表示该区域有那些炮弹,区间合并就是 或 运算
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define Num(i) (1<<(i-1)) 3 using namespace std; 4 5 const int maxn=1e5+5; 6 int l,t,o; 7 inline int get_two(int x){ 8 int ans=0; 9 while(x){ 10 if(x&1)ans++; 11 x>>=1; 12 } 13 return ans; 14 } 15 int sum[maxn<<2],lazy[maxn<<2]; 16 inline void update(int k){ 17 sum[k]=sum[k<<1]|sum[k<<1|1]; 18 } 19 inline void change(int k,int t){ 20 sum[k]=Num(t); 21 lazy[k]=t; 22 } 23 inline void down(int k){ 24 if(lazy[k]){ 25 change(k<<1,lazy[k]); 26 change(k<<1|1,lazy[k]); 27 lazy[k]=0; 28 } 29 } 30 void build(int k,int l,int r){ 31 if(l==r){ 32 change(k,1); 33 return; 34 } 35 else{ 36 int mid=(l+r)>>1; 37 build(k<<1,l,mid); 38 build(k<<1|1,mid+1,r); 39 40 update(k); 41 //printf("l%d r%d sum%d\n",l,r,sum[k]); 42 } 43 } 44 void modify(int k,int l,int r,int x,int y,int t){ 45 if(x<=l&&r<=y){ 46 change(k,t); 47 return; 48 } 49 else{ 50 down(k); 51 52 int mid=(l+r)>>1; 53 54 if(x<=mid)modify(k<<1,l,mid,x,y,t); 55 if(mid<y)modify(k<<1|1,mid+1,r,x,y,t); 56 57 update(k); 58 } 59 } 60 inline int query(int k,int l,int r,int x,int y){ 61 if(x<=l&&r<=y){ 62 return sum[k]; 63 } 64 else{ 65 down(k); 66 67 int ans=0; 68 69 int mid=(l+r)>>1; 70 if(x<=mid)ans|=query(k<<1,l,mid,x,y); 71 if(mid<y)ans|=query(k<<1|1,mid+1,r,x,y); 72 73 return ans; 74 } 75 } 76 char ch[5]; 77 int main(){ 78 scanf("%d%d%d",&l,&t,&o); 79 build(1,1,l); 80 for(int i=1,a,b,c;i<=o;i++){ 81 scanf("%s",ch); 82 if(ch[0]=='C'){ 83 scanf("%d%d%d",&a,&b,&c); 84 if(a>b)swap(a,b); 85 modify(1,1,l,a,b,c); 86 } 87 else{ 88 scanf("%d%d",&a,&b); 89 if(a>b)swap(a,b); 90 printf("%d\n",query(1,1,l,a,b)); 91 } 92 } 93 return 0; 94 }
思考:
当T
——————————基础算法加深——————————
T1:
一道思路挺奇特的题,dijkstra算法是广为人知的最短路算法,但是不能求解带负环的,(同样的,乘法也可以,但是不能有除法环)
所以面对乘数极大的情况时,就要使用(double)log10()函数减少储存的数的大小,
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<cstdlib> 6 #include<cmath> 7 #include<queue> 8 #define LL long long 9 using namespace std; 10 template<typename T> 11 inline void read(T &a){ 12 a=0;T b=1;char x=getchar(); 13 while(x<'0'||'9'<x){ 14 if(x=='-')b=-1; 15 x=getchar(); 16 } 17 while('0'<=x&&x<='9'){ 18 a=(a<<1)+(a<<3)+x-'0'; 19 x=getchar(); 20 } 21 a*=b; 22 } 23 char C_[50]; 24 int TEMP; 25 template<typename T> 26 inline void write(T a){ 27 if(a<0){ 28 putchar('-'); 29 a=-a; 30 } 31 do{ 32 C_[++TEMP]=a%10+'0'; 33 a/=10; 34 }while(a); 35 while(TEMP)putchar(C_[TEMP--]); 36 } 37 const int maxn=1e5+5; 38 const int maxm=5e5+5; 39 40 int s,t,n,m; 41 LL p; 42 43 int fst[maxn],nxt[maxm<<1],to[maxm<<1],edge_count; 44 LL w[maxm<<1]; 45 double l[maxm<<1]; 46 inline void add(int x,int y,LL z){ 47 edge_count++; 48 to[edge_count]=y; 49 50 l[edge_count]=log10(z); 51 /*dijkstra算法乘法(没有除法)也能用; 52 但是受到无法记录路径长 的限制; 53 这时候就需要使用log函数,*变+ ; 54 从而让整个长度变得可以储存 55 */ 56 w[edge_count]=z%p; 57 nxt[edge_count]=fst[x]; 58 fst[x]=edge_count; 59 } 60 priority_queue<pair<double,int > >q; 61 bool vis[maxn]; 62 double dis[maxn]; 63 LL d[maxn]; 64 65 inline void dijkstra(){ 66 for(int i=1;i<=n;i++)dis[i]=100000000.0; 67 dis[s]=0.0;d[s]=1ll; 68 69 q.push(make_pair(-dis[s],s)); 70 while(q.size()){ 71 int u=q.top().second; 72 q.pop(); 73 if(vis[u])continue; 74 75 vis[u]=1; 76 for(int i=fst[u];i;i=nxt[i]){ 77 int v=to[i]; 78 double t=dis[u]+l[i]; 79 80 if(t<dis[v]){ 81 d[v]=d[u]*w[i]%p; 82 dis[v]=t; 83 q.push(make_pair(-dis[v],v)); 84 } 85 } 86 } 87 printf("%lld",d[t]); 88 } 89 int main() 90 { 91 scanf("%d%d%d%d%lld",&n,&m,&s,&t,&p); 92 int a,b;LL c; 93 for(int i=1;i<=m;i++){ 94 scanf("%d%d%lld",&a,&b,&c); 95 add(a,b,c);add(b,a,c); 96 } 97 dijkstra(); 98 return 0; 99 }
3s的时间还是很充裕的,部分分挺水的。
考虑O(nm)算法:
首先的思路是边权为1,我们发现:和最短路经过的起始点不同的次短路正好就是答案
考虑怎么求,显然n遍bfs就可以搞定,但是具体实现的时候有两个注意点:
1.当u的最短路不能更新v时,u的最短路&次短路才能更新v的次短路
2.源点不能再入队,否则就会导致某些节点的次短路为1
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstdlib> 3 #include<algorithm> 4 #include<cstring> 5 #include<iostream> 6 #include<cmath> 7 using namespace std; 8 const int maxn=1e3+5; 9 const int maxm=1e5+5; 10 const int INF=0x3f3f3f3f; 11 int fst[maxn],nxt[maxm],to[maxm],s[maxm],edge_count; 12 inline void add(int x,int y){ 13 edge_count++; 14 s[edge_count]=x; 15 to[edge_count]=y; 16 nxt[edge_count]=fst[x]; 17 fst[x]=edge_count; 18 } 19 int n,m; 20 int ans[maxn][maxn]; 21 int que[maxn*maxn],front,rear,dis[maxn][2],sor[maxn][2]; 22 //dis[i][0/1]表示最/次短路 23 //sor[i][0/1]表示最/次短路一开始经过的(非起点)点 24 inline void bfs(int s){ 25 front=rear=0; 26 que[rear++]=s; 27 28 memset(dis,0x3f,sizeof(dis)); 29 memset(sor,0,sizeof(sor)); 30 31 dis[s][0]=0; 32 //sor[s][0]=s; 33 //问题:1.源点不能再入队。 34 35 while(front<rear){ 36 int u=que[front++]; 37 for(int i=fst[u];i;i=nxt[i]){ 38 int v=to[i]; 39 if(v==s)continue; 40 41 if(dis[u][0]+1<dis[v][0]){ 42 dis[v][0]=dis[u][0]+1; 43 sor[v][0]=( sor[u][0] ? sor[u][0] : v ); 44 que[rear++]=v; 45 //最短路经过第一条边 46 } 47 else{ 48 if(dis[u][0]+1<dis[v][1] && sor[u][0]!=sor[v][0]){ 49 dis[v][1]=dis[u][0]+1; 50 sor[v][1]=sor[u][0]; 51 que[rear++]=v; 52 } 53 if(dis[u][1]+1<dis[v][1] && sor[u][1]!=sor[v][0]){ 54 dis[v][1]=dis[u][1]+1; 55 sor[v][1]=sor[u][1]; 56 que[rear++]=v; 57 } 58 } 59 } 60 } 61 for(int i=fst[s];i;i=nxt[i]){ 62 int v=to[i]; 63 if(dis[v][1]!=INF){ 64 ans[s][v]=dis[v][1]; 65 } 66 } 67 } 68 int main(){ 69 memset(ans,0xff,sizeof(ans)); 70 scanf("%d%d",&n,&m); 71 for(int i=1,a,b;i<=m;i++){ 72 scanf("%d%d",&a,&b); 73 add(a,b); 74 } 75 for(int i=1;i<=n;i++)bfs(i); 76 for(int i=1;i<=m;i++){ 77 printf("%d ",ans[ s[i] ][ to[i] ]); 78 } 79 return 0; 80 } 81 /* 82 想问题要想清楚了,想想等价性 83 比如本题中的次短路 就 不等于平时的次短路 84 需要注意不能从同一个边出发 85 */
no code:看看数据范围就应该能想到这题是个最短路算法,当然肯定是要做些变动的,类比之前的 【newstart 新的开始(点权转边权)】我们能想到建超级源点的做法
本题之所以能建超级源点,就是在于可以有某些点自己走回自己(总代价就剩下了自己的点权),也可以走到别的点
T4:
这题一看就是并查集啊,询问连通性,更关键的是根本没有修改操作
我们维护两种并查集
1.每一行维护一个,表示下次访问跳到哪里
2.整体维护一个,表示是否联通,
最后要注意的是每次修改操作结束后,相邻行列都要扫一下,防止不覆盖造成的不更新(1,1)(1,2)实际上是联通的但是不覆盖
wa code???(敲了2个多小时还没T1 10几分钟写的高):
好吧其实是每个修改操作是一个矩形,可能左上右下,可能右上左下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 template<typename T> 4 inline void read(T &a){ 5 a=0;char x=getchar(); 6 while(x<'0'||'9'<x)x=getchar(); 7 while('0'<=x&&x<='9'){ 8 a=(a<<1)+(a<<3)+x-'0'; 9 x=getchar(); 10 } 11 } 12 const int maxr=55; 13 const int maxc=1e5+5; 14 const int r=50,c=100000; 15 inline int trans(int x,int y){ 16 return (x-1)*c+y; 17 } 18 int f[maxr*maxc],size[maxr*maxc]; 19 int find(int x){return (f[x]==x) ? x : f[x]=find(f[x]) ;}//dsu 20 inline void dsu_init(){for(int i=1;i<=5000000;i++)f[i]=i,size[i]=1;} 21 inline void merge(int u,int v){ 22 int fu=find(u),fv=find(v); 23 if(fu==fv)return; 24 if(size[fu]<size[fv]){//启发式合并 25 f[fu]=fv; 26 size[fv]+=size[fu]; 27 } 28 else{ 29 f[fv]=fu; 30 size[fu]+=size[fv]; 31 } 32 } 33 int nxt[maxr][maxc]; 34 int jump(int x,int y){return (nxt[x][y]==y) ? y : nxt[x][y]=jump(x,nxt[x][y]) ;}//dsu2 35 inline void jump_init(){for(int i=1;i<=r;i++)for(int j=1;j<=c;j++)nxt[i][j]=j;} 36 int q,opt,X1,X2,Y1,Y2; 37 int main(){ 38 //freopen("dawn.in","r",stdin); 39 //freopen("dawn.out","w",stdout); 40 41 dsu_init(); 42 jump_init(); 43 read(q); 44 while(q--){ 45 read(opt);read(X1);read(Y1);read(X2);read(Y2); 46 if(opt){//查询操作 47 if(X1==X2 && Y1==Y2)printf("%d\n",(nxt[X1][Y1]!=Y1));//被修改过,当然能走,未被修改,不能走 48 else printf("%d\n",( find(trans(X1,Y1) )==find( trans(X2,Y2) ) ) ); 49 } 50 else{ 51 for(int i=X1;i<=X2;i++) 52 for(int j=max(Y1,jump(i,Y1)-1);j<=Y2;j=max(j+1,jump(i,j)-1)){ 53 merge(trans(i,j),trans(X2,Y2)); 54 nxt[i][j]=j+1; 55 } 56 57 int i=X1-1; 58 if(i)for(int j=max(Y1,jump(i,Y1)-1);j<=Y2;j=max(j+1,jump(i,j)-1)){ 59 60 if(nxt[i][j]!=j)merge(trans(i,j),trans(X2,Y2)); 61 } 62 i=X2+1; 63 if(i<=r)for(int j=max(Y1,jump(i,Y1)-1);j<=Y2;j=max(j+1,jump(i,j)-1)){ 64 if(nxt[i][j]!=j)merge(trans(i,j),trans(X2,Y2)); 65 } 66 67 int yy=Y1-1,yy_=Y2+1; 68 for(int j=X1;j<=X2;j++){ 69 if(yy)if(nxt[j][yy]!=yy)merge(trans(j,yy),trans(X2,Y2)); 70 if(yy_<=c)if(nxt[j][yy_]!=yy_)merge(trans(j,yy_),trans(X2,Y2)); 71 }//相邻情况 72 } 73 } 74 return 0; 75 }
常数问题还需优化...
——————————矩阵加速递推shit题集锦——————————
T1
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<cstdlib> 6 #include<cmath> 7 #define LL long long 8 using namespace std; 9 template<typename T> 10 inline void read(T &a){ 11 a=0;T b=1;char x=getchar(); 12 while(x<'0'||'9'<x){ 13 if(x=='-')b=-1; 14 x=getchar(); 15 } 16 while('0'<=x&&x<='9'){ 17 a=(a<<1)+(a<<3)+x-'0'; 18 x=getchar(); 19 } 20 a*=b; 21 } 22 char C_[50]; 23 int TEMP; 24 template<typename T> 25 inline void write(T a){ 26 if(a<0){ 27 putchar('-'); 28 a=-a; 29 } 30 do{ 31 C_[++TEMP]=a%10+'0'; 32 a/=10; 33 }while(a); 34 while(TEMP)putchar(C_[TEMP--]); 35 } 36 37 /* 38 main thought: 39 数位DP 40 O(n)算法就是递推了,f[i][j]表示k进制下, 41 到第i位 数位和 %60余j的方案数 42 所以想到矩阵加速递推,O(60^2*log(n)) 43 */ 44 const LL P=1e9+7; 45 const int maxn=65; 46 47 LL sum[maxn]; 48 struct Matrix{ 49 50 LL a[maxn][maxn]; 51 int n,m; 52 Matrix(int N,int M){ 53 n=N;m=M; 54 memset(a,0,sizeof(a)); 55 } 56 Matrix operator *(const Matrix&y)const{//(n,m)*(m,k)=(n,k) 57 Matrix ans(60,60); 58 int k=y.m; 59 for(int i=0;i<n;i++) 60 for(int j=0;j<k;j++) 61 for(int l=0;l<m;l++) 62 ans.a[i][j]=(ans.a[i][j]+a[i][l]*y.a[l][j]%P)%P; 63 return ans; 64 } 65 Matrix operator -(const Matrix &y)const{ 66 Matrix ans(n,m); 67 for(int i=0;i<n;i++) 68 for(int j=0;j<m;j++) 69 ans.a[i][j]=((a[i][j]-y.a[i][j])%P+P)%P; 70 return ans; 71 } 72 inline void print(){ 73 for(int i=0;i<n;i++){ 74 for(int j=0;j<m;j++)printf("%lld ",a[i][j]); 75 printf("\n"); 76 } 77 printf("-----------------"); 78 } 79 }; 80 LL l,r,k; 81 inline void quickpow(Matrix &A,Matrix B,LL n){ 82 while(n){ 83 if(n&1ll)A=A*B; 84 B=B*B; 85 n>>=1; 86 } 87 } 88 int main() 89 { 90 //freopen("2.out","w",stdout); 91 92 scanf("%lld%lld%lld",&l,&r,&k); 93 //if(k>(1ll<<32))printf("%lld %lld %lld ",l,r,k); 94 95 for(LL i=(k-1)%60ll;i>=0ll;i--)sum[i]=((k-1)/60ll+1ll)%P; 96 for(LL i=(k-1)%60ll+1;i<60ll;i++)sum[i]=((k-1)/60ll)%P; 97 //for(int i=0;i<60;i++)printf("%lld\n",sum[i] ); 98 99 100 Matrix Al(1,60); 101 Matrix Ar(1,60); 102 for(int i=0;i<60;i++){ 103 Ar.a[0][i]=Al.a[0][i]=sum[i];//同余i的方案数 104 } 105 //Ar.print(); 106 107 Matrix B(60,60); 108 for(int i=0;i<60;i++)for(int j=0;j<60;j++){ 109 B.a[j][i]=sum[((i-j)%60+60)%60]; 110 } 111 //B.print(); 112 quickpow(Ar,B,r-1ll); 113 if(l==1ll){ 114 LL ans=0ll; 115 for(int i=0;i<60;i++){ 116 if(i%4==0 || i%5==0 || i%6==0)ans=(ans+Ar.a[0][i])%P; 117 } 118 printf("%lld",ans); 119 return 0; 120 } 121 122 quickpow(Al,B,l-2ll); 123 124 Ar=Ar-Al; 125 LL ans=0ll; 126 for(int i=0;i<60;i++){ 127 if(i%4==0 || i%5==0 || i%6==0)ans=(ans+Ar.a[0][i])%P; 128 } 129 printf("%lld",ans); 130 return 0; 131 } 132 /* 133 953896363319480814 997835046073951360 924421070731383047 134 */
Matrix的板子要多打...尤其是构造矩阵那里
T2
T5 P5343
DP+矩阵加速递推
DP式O(n)的很好求,dp[n]= ∑dp[n-k],DA[k]=1&&SA[k]=1
由于k<=100他其实是线性的
而线性递推加速考虑矩阵加速递推
{
dp[n]= ∑dp[n-k]
dp[n-1]=dp[n-1]
.
.
.
dp[n-99]=dp[n-99]
}
所以可以构造矩阵
矩阵第一行的表示有哪些可以取的k
于是快速幂就可以过了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<cstdlib> 6 #include<cmath> 7 #define LL long long 8 using namespace std; 9 template<typename T> 10 inline void read(T &a){ 11 a=0;bool b=0;char x=getchar(); 12 while(x<'0'||'9'<x){ 13 if(x=='-')b=1; 14 x=getchar(); 15 } 16 while('0'<=x&&x<='9'){ 17 a=(a<<1)+(a<<3)+x-'0'; 18 x=getchar(); 19 } 20 if(b)a=-a; 21 } 22 char C_[50]; 23 int TEMP; 24 template<typename T> 25 inline void write(T a){ 26 if(a<0){ 27 putchar('-'); 28 a=-a; 29 } 30 do{ 31 C_[++TEMP]=a%10+'0'; 32 a/=10; 33 }while(a); 34 while(TEMP)putchar(C_[TEMP--]); 35 } 36 const LL P=1e9+7; 37 const int maxn=1e2+5; 38 struct Matrix{ 39 int n,m; 40 LL a[maxn][maxn]; 41 Matrix(int N,int M){ 42 n=N;m=M; 43 memset(a,0,sizeof(a)); 44 } 45 Matrix operator*(const Matrix&x)const{ 46 int k=x.m; 47 Matrix ans(n,k); 48 for(int i=1;i<=n;i++) 49 for(int j=1;j<=k;j++) 50 for(int l=1;l<=m;l++) 51 ans.a[i][j]=(ans.a[i][j]+a[i][l]*x.a[l][j])%P; 52 return ans; 53 } 54 }; 55 int da,sa; 56 LL n; 57 bool d[maxn],s[maxn]; 58 inline void quickpow(Matrix &A,Matrix B,LL n){ 59 while(n){ 60 if(n&1ll)A=A*B; 61 B=B*B; 62 n>>=1; 63 } 64 } 65 int main(){ 66 scanf("%lld",&n); 67 scanf("%d",&da); 68 for(int i=1;i<=da;i++){ 69 int x; 70 scanf("%d",&x); 71 d[x]=1; 72 } 73 scanf("%d",&sa); 74 for(int i=1;i<=sa;i++){ 75 int x; 76 scanf("%d",&x); 77 s[x]=1; 78 } 79 Matrix A(1,100);A.a[1][1]=1;//f[0]=1 80 Matrix B(100,100); 81 for(int i=1;i<=100;i++){ 82 B.a[i][1]=(d[i] & s[i]); 83 B.a[i-1][i]=1; 84 } 85 quickpow(A,B,n); 86 printf("%lld",A.a[1][1]); 87 return 0; 88 }
(一遍就AC了,开森)
——————————二分+稍有难度的贪心验证——————————
T1:
一开始想的是虚树+二分,但是数据范围告诉我们虚树没啥用
后来想到了二分不过不幸的是不会验证
下面讲一下这种东西怎么验证:
1.看链:
对于链来说,验证显然贪心就可以,从左到右扫描,尽量放在最右边才能覆盖最多的点
2.进化成树:
我们暂且考虑 结点u 只覆盖子树内的关键结点
由于深度每次增加1,所以u必须放 装置 当且仅当有一个子树内的关键节点距离u为len 否则之后的结点再也不可能覆盖他了
所以贪心是正确的,证毕
Code:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<cstdlib> 6 #include<cmath> 7 #define LL long long 8 using namespace std; 9 template<typename T> 10 inline void read(T &a){ 11 a=0;T b=1;char x=getchar(); 12 while(x<'0'||'9'<x){ 13 if(x=='-')b=-1; 14 x=getchar(); 15 } 16 while('0'<=x&&x<='9'){ 17 a=(a<<1)+(a<<3)+x-'0'; 18 x=getchar(); 19 } 20 a*=b; 21 } 22 char C_[50]; 23 int TEMP; 24 template<typename T> 25 inline void write(T a){ 26 if(a<0){ 27 putchar('-'); 28 a=-a; 29 } 30 do{ 31 C_[++TEMP]=a%10+'0'; 32 a/=10; 33 }while(a); 34 while(TEMP)putchar(C_[TEMP--]); 35 } 36 const int maxn=2e5+5; 37 int fst[maxn],nxt[maxn<<1],to[maxn<<1],edge_count; 38 inline void add(int x,int y){ 39 edge_count++; 40 to[edge_count]=y; 41 nxt[edge_count]=fst[x]; 42 fst[x]=edge_count; 43 } 44 int dep[maxn][3]; 45 //dep[i][0]表示以i位根的子树内最深的没被覆盖的关键节点 46 //dep[i][1]表示以i为根的字数内最浅的放置的发射点 47 int n,m,k,ans,cnt; 48 bool imp[maxn]; 49 void dfs(int u,int fa){ 50 dep[u][2]=dep[fa][2]+1; 51 52 if(imp[u])dep[u][0]=dep[u][2]; 53 for(int i=fst[u];i;i=nxt[i]){ 54 int v=to[i]; 55 if(v==fa)continue; 56 57 dfs(v,u); 58 if(!dep[u][0])dep[u][0]=dep[v][0]; 59 else if(dep[v][0])dep[u][0]=max(dep[u][0],dep[v][0]); 60 if(!dep[u][1])dep[u][1]=dep[v][1]; 61 else if(dep[v][1])dep[u][1]=min(dep[u][1],dep[v][1]); 62 } 63 64 if(dep[u][0]+dep[u][1]-dep[u][2]-dep[fa][2]>=ans || (u==1 && dep[u][0])){ 65 dep[u][0]=0; 66 dep[u][1]=dep[u][2]; 67 cnt++; 68 } 69 } 70 inline bool check(int x){ 71 ans=x; 72 cnt=0; 73 for(int i=1;i<=n;i++)dep[i][0]=dep[i][1]=0; 74 dfs(1,0); 75 return cnt<=k; 76 } 77 int main(){ 78 read(n);read(m);read(k); 79 for(int i=1,x;i<=m;i++)read(x),imp[x]=1; 80 for(int i=1,u,v;i<n;i++)read(u),read(v),add(u,v),add(v,u); 81 82 int left=0,right=n; 83 int mid=left+right>>1; 84 while(left+1<right){ 85 if(check(mid))right=mid; 86 else left=mid; 87 mid=left+right>>1; 88 } 89 if(check(left))write(left); 90 else write(right); 91 return 0; 92 }
注意:二分答案往往会有很明显得提示,关键还是如何验证的问题:大多是是贪心,少部分是神奇数据结构,而且确定答案之后的验证多了一“迫不得已”的限制,这就让我们的贪心更加顺利的进行
技巧:
往往有些时候没有强大的数据结构帮我们完成大范围的 修改,查询 这时候考虑从最大or最小值得维护上 出发就会好想一些,
T2:
apple(???)
首先答案是单调的(可二分的最重要前提)如果直接贪心的话我们不知道 接收器 到底放不放
放->可能会有放中继器更优的解
不放->可能以后就没机会放了
但是我们二分的话就可以避免这些问题
如果只放num个,显然 要放前num大的b[i]
而且只有【迫不得已】(引自某题解) 时才放接收器显然是最优的
当然放中继器的话一定可大的来,最后对答案贡献最大
【有个小小的鬼畜错误:check() 里面 if(q==0)break; 就会wa一个点,不加这句话就Ac】
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 using namespace std; 5 /* 6 答案单调的,所以二分,验证就是放 前几个接受能力强的, 7 并且迫不得已时才放 8 !!!(和直接求解的最大区别:必须放 9 不用考虑是否不放会更优)!!! 10 */ 11 12 const int maxn=2e5+5; 13 int n,m,a[maxn],b[maxn]; 14 inline bool check(int num){//验证答案 15 int cnt=1;//可用节点数 16 int dep=0;//当前深度 17 int p=m-num+1;//接受点指针 18 int q=n;//中继点指针,将要放 19 20 while(p<=m && cnt){ 21 //cout<<p<<" "<<q<<" "<<cnt<<" "<<dep<<endl; 22 if(b[p]==dep){//不得不放 23 p++; 24 cnt--; 25 continue; 26 } 27 int sum=0; 28 while(cnt && q){//优先放拓展最快的 29 sum+=a[q--]; 30 cnt--; 31 } 32 cnt+=sum; 33 dep++; 34 } 35 return p>m; 36 } 37 int main(){ 38 scanf("%d%d",&n,&m); 39 for(int i=1;i<=n;i++)scanf("%d",&a[i]); 40 for(int i=1;i<=m;i++)scanf("%d",&b[i]); 41 sort(a+1,a+n+1);sort(b+1,b+m+1); 42 43 int l=0,r=m,mid; 44 mid=(l+r)>>1; 45 while(l+1<r){ 46 //printf("%d %d\n",mid,check(mid)); 47 if(check(mid))l=mid; 48 else r=mid; 49 50 mid=(l+r)>>1; 51 } 52 if(check(r))printf("%d",r); 53 else printf("%d",l); 54 return 0; 55 }
本以为偏差是绝对值,但是没想到竟然不是,所以偏差越大时间越大,二分就好了
no code
——————————数据结构加深——————————
T1:
【bzoj4010】[HNOI2015]菜肴制作 拓扑排序+堆
题目大意:求编号位置序最小(每个编号所在位置最小)
方法1:
建链表,每次把需要放在他前面的放前面,递归去找,最后的表应该就是答案,但是他好像是错的
30·
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
希望dalao们想出反例...
。。。艹。。。Rechardluan竟然在几分钟之内就想到反例了
我的算法是基于保证比x小的节点都在x前面,但是问题就在于 每次处理的序列并不一定符合题目要求(不完全等价),比如一组数据是
5 3
5 2
4 2
3 5
12345 -> 1 45 23 -> 14 3 52
但是正确答案应该是
13452
正解:
一个数的位置确定,当且仅当比这个数大的 以及 必须在他后面的 都在他后面(不能把 比他小的放前面,会有问题)
所以我们会想到建反图,每次找最大的 零度节点 删除
最后倒序输出就是答案,完美的避开了之前说的递归区间会有的问题
std:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
总结:
1.证明算法的复杂度。2.证明算法的正确性(往往贪心算法都需要想反例,从算法的 每一步 去思考,想出可能会造成问题的环节)
T2:
【CF????】
题目大意:
给出n个数an,ai表示位置i的最长上升子序列长度,求出合法的1~n的排列 s.t. 条件成立
题解:
像上道题一样,我们考虑什么时候才能确定该位置数的大小:
首先,[ai]是由[ai-1]转移来的,并且,在众多的[ai]中,一定是从后往前越来越大,所以不难想到,将ai-1 连向 ai ,topological_sort一下就好了,每次选位置i最大的
no code...(口头AC一波)
T3:Continuous Intervals【segtree + 单调栈】
给出n个数的序列。问序列中有多少个区间满足,排序完之后任意两个相邻的数之差不大于1。
往往这种问题需要转化成区间修改区间查询问题,所以可以转化成
用max表示区间最大值,min表示区间最小值,cnt表示区间数字的种数。那么问题转化成求max-min=cnt+1的区间数。
用线段树维护每个区间的max-min-cnt最小值及最小值的个数,不用单独维护max,min和cnt。注意max-min >= cnt+1.
从1~n枚举R。对于每个枚举的R,更新以R为后缀的[L,R]区间的max-min-cnt值。
对于max和min可以用单调栈维护,max和min对max-min-cnt的贡献采用区间加减的形式而不是区间覆盖。
对于cnt可以用一个vis[]数组维护上一次的出现位置,然后也进行区间加减。数据是1e9的,vis[]数组可以离散一下或者用map代替。
最后对于每一个枚举的R,统计max-min-cnt的值为-1的[L,R]数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<iostream> 5 #include<algorithm> 6 #define LL long long 7 using namespace std; 8 const int maxn=1e5+5; 9 struct Node{//min(max-min-cnt)= -1 故统计答案的时候只需要统计最小值及最小值数目 10 int mn,lazy,cnt; 11 inline void modify(int x,int l,int r){//区间覆盖 12 mn+=x; 13 lazy+=x; 14 } 15 inline void update(const Node&a,const Node&b){ 16 if(a.mn==b.mn){ 17 mn=a.mn,cnt=a.cnt+b.cnt; 18 } 19 else if(a.mn<b.mn){ 20 mn=a.mn,cnt=a.cnt; 21 } 22 else{ 23 mn=b.mn,cnt=b.cnt; 24 } 25 } 26 }L[maxn<<2]; 27 inline void down(int k,int l,int r){ 28 if(L[k].lazy){ 29 int mid=(l+r)>>1; 30 L[k<<1].modify(L[k].lazy,l,mid); 31 L[k<<1|1].modify(L[k].lazy,mid+1,r); 32 L[k].lazy=0; 33 } 34 } 35 void modify(int k,int l,int r,int x,int y,int xx){ 36 if(x<=l&&r<=y){ 37 return L[k].modify(xx,l,r); 38 } 39 else{ 40 down(k,l,r); 41 42 int mid=(l+r)>>1; 43 if(x<=mid)modify(k<<1,l,mid,x,y,xx); 44 if(mid<y)modify(k<<1|1,mid+1,r,x,y,xx); 45 46 return L[k].update(L[k<<1],L[k<<1|1]); 47 } 48 } 49 Node query(int k,int l,int r,int x,int y){ 50 if(x<=l&&r<=y){ 51 return L[k]; 52 } 53 else{ 54 down(k,l,r); 55 56 int mid=(l+r)>>1; 57 if(y<=mid)return query(k<<1,l,mid,x,y); 58 else if(mid<x)return query(k<<1|1,mid+1,r,x,y); 59 else{ 60 Node ans={1<<30,0,0}; 61 ans.update(query(k<<1,l,mid,x,y),query(k<<1|1,mid+1,r,x,y)); 62 return ans; 63 } 64 } 65 } 66 void build(int k,int l,int r){ 67 if(l==r){ 68 L[k].mn=0; 69 L[k].lazy=0; 70 L[k].cnt=1; 71 } 72 else{ 73 int mid=(l+r)>>1; 74 build(k<<1,l,mid); 75 build(k<<1|1,mid+1,r); 76 77 return L[k].update(L[k<<1],L[k<<1|1]); 78 } 79 } 80 int n,a[maxn],b[maxn],pre[maxn]; 81 82 struct number{ 83 int i,num; 84 const bool operator<(const number&x)const{ 85 return num<x.num; 86 } 87 }c[maxn]; 88 inline void discrete(){ 89 for(int j=1;j<=n;j++){ 90 c[j].i=j;c[j].num=a[j]; 91 } 92 sort(c+1,c+n+1); 93 for(int j=1;j<=n;j++){ 94 if(c[j].num==c[j-1].num)b[ c[j].i ]=b[ c[j-1].i ]; 95 else b[ c[j].i ]=b[ c[j-1].i ]+1; 96 } 97 } 98 int T; 99 int sta[maxn][2],p[2]; 100 LL ans; 101 inline void clear(){ 102 memset(pre,0,sizeof(pre)); 103 memset(L,0,sizeof(L)); 104 p[0]=p[1]=0; 105 ans=0ll; 106 } 107 int main(){ 108 109 scanf("%d",&T); 110 while(T--){ 111 clear(); 112 scanf("%d",&n); 113 build(1,1,n); 114 for(int i=1;i<=n;i++){ 115 scanf("%d",&a[i]); 116 } 117 discrete(); 118 p[0]=0;//单调递增栈维护 左侧 较小值 119 p[1]=0;//单调递增栈维护 左侧 较大值 120 for(int i=1;i<=n;i++){ 121 while(a[ sta[p[0]][0] ]>a[i] && p[0]){ 122 modify(1,1,n,sta[p[0]-1][0]+1,sta[p[0]][0],-a[i]+a[ sta[p[0]][0] ]); 123 p[0]--; 124 } 125 sta[++p[0]][0]=i; 126 127 while(a[ sta[p[1]][1] ]<a[i] && p[1]){ 128 modify(1,1,n,sta[p[1]-1][1]+1,sta[p[1]][1],a[i]-a[ sta[p[1]][1] ]); 129 p[1]--; 130 } 131 sta[++p[1]][1]=i; 132 133 modify(1,1,n,pre[ b[i] ]+1,i,-1); 134 pre[ b[i] ]=i; 135 136 Node k=query(1,1,n,1,i); 137 if(k.mn==-1)ans+=k.cnt; 138 } 139 printf("%lld\n",ans); 140 } 141 return 0; 142 }
注:往往区间类问题可以 考虑 合法的区间对答案的贡献 以及等价变形,然后变成一个单点(区间)修改区间查询的问题
T4:
xiaoqiao
没想到O(nlogn^2)竟然能过:
想到了树状数组+二分查找,但是正解 是平衡树(节操呢?)
最后有一个坑点就是,不保证所有的s<t,所以要进行特判,当s>t时要分裂成两个区间
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cmath> 3 #include<cstring> 4 #include<algorithm> 5 #include<iostream> 6 #include<cstdlib> 7 #define LL long long 8 using namespace std; 9 const int maxn=1e5+5; 10 inline int read(){ 11 int a=0;bool b=0;char x=getchar(); 12 while(x<'0' || '9'<x){ 13 if(x=='-')b=1; 14 x=getchar(); 15 } 16 while('0'<=x && x<='9'){ 17 a=(a<<1)+(a<<3)+x-'0'; 18 x=getchar(); 19 } 20 return b ? -a : a ; 21 } 22 int n,m,k,maxr; 23 /* 24 二分查找+树状数组区间修改单点查询 25 O(2n logn^2) 26 */ 27 int sum[maxn]; 28 inline int lowbit(int x){return x&(-x);} 29 inline void modify(int p,int x){ 30 while(p){ 31 sum[p]+=x; 32 p-=lowbit(p); 33 } 34 } 35 inline int query(int p){ 36 int ans=0; 37 while(p<=maxr){ 38 ans+=sum[p]; 39 p+=lowbit(p); 40 } 41 return ans; 42 } 43 inline int find(int x){//二分查找,>=x 的最右边位置 44 int left=0,right=maxr; 45 int mid=left+right>>1; 46 47 while(left+1<right){ 48 if(query(mid)>=x)left=mid; 49 else right=mid; 50 mid=left+right>>1; 51 } 52 if(query(right)>=x)return right; 53 else return left; 54 } 55 int cnt; 56 struct wind{ 57 int p,r,x; 58 inline bool operator<(const wind&a)const{ 59 return p<a.p; 60 } 61 wind(int P=0,int R=0,int X=0):p(P),r(R),x(X){} 62 }w[maxn<<2]; 63 LL ans; 64 int main(){ 65 //O(nlogn^2)算法的极限是多少??? N e 7 ??? 66 n=read();m=read();k=read(); 67 for(int i=1,r,s,t;i<=n;i++){ 68 r=read();s=read();t=read(); 69 if(s>t){ 70 w[++cnt]=wind(-m,r,1); 71 w[++cnt]=wind(m,r,-1); 72 } 73 74 w[++cnt]=wind(t,r,-1); 75 w[++cnt]=wind(s,r,1); 76 maxr=max(maxr,r); 77 } 78 sort(w+1,w+cnt+1); 79 for(int i=-m,p=1;i<=m;i++){ 80 while(w[p].p==i && p<=cnt){ 81 modify(w[p].r,w[p].x); 82 p++; 83 } 84 LL t=find(k); 85 ans+=t*t; 86 } 87 printf("%lld",ans); 88 return 0; 89 }
T5:
1.序列
(array.cpp/c/pas)
【问题描述】
对于一个给定的非负序列{a1,a2,…,an},我们称在1≤i,j≤n时满足k⋅|i−j|≤min(ai,aj)的k为这个序列的扩张指数。
你的任务就是对于某个序列求出它的扩张指数
数据保障一定存在一个扩张指数。
【输入格式】
输入文件名为array.in。
输入的第一行为一个正整数n,代表这个序列元素的个数。
输入的第二行为n个正整数,代表该序列某位置上元素的值。
【输出格式】
输入文件名为array.out。
输入的第一行为一个正整数k,代表这个序列的扩张指数。
【输入输出样例 1】
array.in |
array.out |
4 6 4 5 5 |
1 |
见选手目录下的array/array1.in和array/array1.ans
【输入输出样例 2】
array.in |
array.out |
4 821 500 479 717 |
239 |
见选手目录下的 array/array2.in和 array/array2.ans。
【数据规模与约定】
对于30%的数据满足n≤1000。
对于另外10%的数据满足序列中数字全部相同。
对于100%的数据满足n≤300000。
正解写的让人作呕,题解粘上,有能看懂的dalao讲给我听好不好???
【前方高能】
第一题
对于数列 a1,a2,…,an,分析其中任意一项ai,考虑所有的aj >= ai(aj < ai的算在aj里面考虑了),则使得min(ai,aj) = ai,要使所有的j都满足k*|i-j| <= min(ai,aj) = ai,则对于ai来说,j应该尽量远离i,这样解出来的最大的k才是有用的(即对ai的k值的约束最紧)。显然最远的j应该是数列两端中的一端,但两端不一定大于ai。那怎么办呢?仔细一想,对于两端的情况,考虑最前端a1及最后端an:
1. 如果a1 >= ai,则对于当前ai来说,k要满足k <= ai/|i-1|(如果i == 1,则说明k值没有限制)。如果a1 < ai,则对于当前ai来说,k要满足k <= a1/|i-1| < ai/|i-1|。
2. 如果an >= ai,则对于当前ai来说,k要满足k <= ai/|i-n|(如果i == n,则说明k值没有限制)。如果an < ai,则对于当前ai来说,k要满足k <= an/|i-n| < ai/|i-n|。
3. 在所有大于等于ai的aj中,k对于ai来说要满足k <= ai/|i-j|,而|i-j| <= |i-1|或|i-j| <= |i-n|,故ai/|i-1| <= ai/|i-j|或ai/|i-n| <= ai/|i-j|。
可见,对每项ai来说,考虑两端的项所解出来的k值必定有一项是有用的(即对ai的k值的约束最紧)。
枚举每一项求解出来的有用的k值,再取最小即得到最优答案。
【身后高能】
然后我只会写数据结构优化啊,,,后来发现segtree的常数大约是树状数组的5~10倍左右(血的教训啊啊啊啊啊啊啊啊啊啊啊啊啊啊。。。)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstdlib> 6 #include<iostream> 7 using namespace std; 8 const int maxn=3e5+5; 9 const int INF=1e9; 10 /* 11 对于本题,我们发现更新ans的数对和顺序无关,所以只从最小的一边得出答案(扫两边即可) 12 而且想到如果每次只添加一个的话,就需要找到之前添加的比他大的数中下标最大(小)即可 13 */ 14 int n,a[maxn],ans; 15 struct number{ 16 int num,i; 17 inline bool operator<(const number&x)const{ 18 return num<x.num; 19 } 20 }b[maxn]; 21 int c[maxn]; 22 inline void Tj1(){ 23 for(int i=1;i<=n;i++)b[i].num=a[i],b[i].i=i; 24 sort(b+1,b+n+1); 25 for(int i=1;i<=n;i++){ 26 if(b[i].num==b[i-1].num)c[ b[i].i ]=c[ b[i-1].i ]; 27 else c[ b[i].i ]=c[ b[i-1].i ]+1; 28 } 29 } 30 inline int lowbit(int x){return x&(-x);} 31 int mx[maxn],mn[maxn]; 32 inline void update_max(int p,int x){// 33 while(p<=n){ 34 mx[p]=max(mx[p],x); 35 p+=lowbit(p); 36 } 37 } 38 inline void update_min(int p,int x){ 39 while(p<=n){ 40 mn[p]=min(mn[p],x); 41 p+=lowbit(p); 42 } 43 } 44 inline int query_min(int p){ 45 int ans=INF; 46 while(p){ 47 ans=min(ans,mn[p]); 48 p-=lowbit(p); 49 } 50 return ans; 51 } 52 inline int query_max(int p){ 53 int ans=0; 54 while(p){ 55 ans=max(mx[p],ans); 56 p-=lowbit(p); 57 } 58 return ans; 59 } 60 inline void clear(){ 61 for(int i=1;i<=n;i++)mn[i]=INF,mx[i]=0; 62 } 63 int main(){ 64 freopen("array.in","r",stdin); 65 freopen("array.out","w",stdout); 66 67 scanf("%d",&n); 68 ans=INF; 69 for(int i=1;i<=n;i++)scanf("%d",&a[i]); 70 Tj1(); 71 clear(); 72 for(int i=1;i<=n;i++){ 73 int pos=query_min(n-c[i]+1);//反转 求后缀 74 if(pos!=INF)ans=min(ans,a[i]/(i-pos)); 75 update_min(n-c[i]+1,i); 76 } 77 clear(); 78 for(int i=n;i;i--){ 79 int pos=query_max(n-c[i]+1); 80 if(pos)ans=min(ans,a[i]/(pos-i)); 81 update_max(n-c[i]+1,i); 82 } 83 printf("%d",ans); 84 return 0; 85 }
std(???):貌似不用折叠...
#include<cstdio> #include<algorithm> using namespace std; int n,ans=~0u>>1,d; int a[500005]; int main(){ freopen("array.in","r",stdin); freopen("array.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",&a[i]); for(int i=1;i<=n;++i){ d=max(i-1,n-i); ans=min(ans,a[i]/d); } printf("%d",ans); return 0; }
update【2020.6.19】:时隔快1年,我再次看自己当年的blog,发现当年的自己挺nb的,再次看这个题,发现了个大秘密,首先考虑那个min,和不等号的方向,可以发现,a[i],a[j]中max值对k的限制 比 min值对k的限制松,所以max值算进来也不会影响答案.
由于每个点都会存在于每对数对中,所以我们枚举一下从a[j] j=1~n 发现a[i]的限制最严的时候一定是在j=1 or n的时候,于是O(n)就做出来了。
刚刚看题竟然没想的自己曾经的做法。
T6:
code??:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #include<cstdlib> 6 #define LL long long 7 using namespace std; 8 const int maxn=5e5+5; 9 struct segtree{ 10 int sum,lazy,len; 11 segtree(int Sum=0,int Lazy=0,int Len=0):sum(Sum),lazy(Lazy),len(Len){} 12 inline void add(int x,int l,int r){ 13 len=r-l+1; 14 sum+=x*len; 15 lazy+=x; 16 } 17 inline void update(const segtree&S1,const segtree&S2){ 18 sum=S1.sum+S2.sum; 19 len=S1.len+S2.len; 20 } 21 }t[maxn<<2]; 22 inline void down(int k,int l,int r){ 23 if(t[k].lazy){ 24 int mid=l+r>>1; 25 t[k<<1].add(t[k].lazy,l,mid); 26 t[k<<1|1].add(t[k].lazy,mid+1,r); 27 t[k].lazy=0; 28 } 29 } 30 struct Sec{ 31 int x,y1,y2,a; 32 Sec(int X=0,int Y1=0,int Y2=0,int A=0):x(X),y1(Y1),y2(Y2),a(A){} 33 const bool operator<(const Sec&S){ 34 return x<S.x; 35 } 36 }s[maxn<<1]; 37 struct Query{ 38 int x,y1,y2,a; 39 Query(int X=0,int Y1=0,int Y2=0,int A=0):x(X),y1(Y1),y2(Y2),a(A){} 40 const bool operator<(const Query&Q){ 41 return x<Q.x; 42 } 43 }q[maxn<<1]; 44 void modify(int k,int l,int r,int x,int y,int z){ 45 if(x<=l&&r<=y){ 46 return t[k].add(z,l,r); 47 } 48 else{ 49 down(k,l,r); 50 51 int mid=l+r>>1; 52 if(x<=mid)modify(k<<1,l,mid,x,y,z); 53 if(mid<y)modify(k<<1|1,mid+1,r,x,y,z); 54 return t[k].update(t[k<<1],t[k<<1|1]); 55 } 56 } 57 segtree query(int k,int l,int r,int x,int y){ 58 if(x<=l&&r<=y){ 59 return t[k]; 60 } 61 else{ 62 down(k,l,r); 63 64 int mid=l+r>>1; 65 if(mid<x)return query(k<<1|1,mid+1,r,x,y); 66 else if(y<=mid)return query(); 67 } 68 } 69 int n,m,w,l; 70 int x1,x2,y2,y1; 71 int main(){ 72 scanf("%d%d%d%d",&n,&m,&w,&l); 73 for(int i=1;i<=n;i++){ 74 scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 75 s[i]=Sec(x1,y1,y2,1); 76 s[i+n]=Sec(x2,y1,y2,-1); 77 } 78 sort(s+1,s+(n<<1)+1); 79 for(int i=1;i<=m;i++){ 80 scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 81 q[i]=Query(x1,y1,y2,-1); 82 q[i+n]=Query(x2,y1,y2,1); 83 } 84 sort(q+1,q+(m<<1)+1); 85 int l=1; 86 build(1,1,l); 87 for(int i=1;i<=w;i++){ 88 89 } 90 return 0; 91 }
T7:
可持久化trie树裸题。。。
暴力的话就不用说了,一种写法是莫队trie树,60`
还有一种写法是离线,记录经过每个节点的数的下标最大是多少
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #include<cmath> 6 #include<cstdlib> 7 using namespace std; 8 const int maxn=2e5+5; 9 int to[maxn<<5][2],rmax[maxn<<5],cnt; 10 inline void insert(int x,int i){ 11 int u=0; 12 for(int j=29;j>=0;j--){ 13 int v=(x>>j)&1; 14 if(!to[u][v])to[u][v]=++cnt; 15 u=to[u][v]; 16 rmax[u]=max(rmax[u],i); 17 } 18 } 19 inline int query(int x,int l){ 20 int ans=0; 21 int u=0; 22 for(int j=29;j>=0;j--){ 23 int v=(x>>j)&1;//取出该位 24 if(to[u][v^1] && rmax[ to[u][v^1] ]>=l){ 25 ans^=(1<<j); 26 v^=1; 27 } 28 u=to[u][v]; 29 } 30 return ans; 31 } 32 int n,Q; 33 int a[maxn],b[maxn]; 34 struct Query{ 35 int l; 36 int r; 37 int x; 38 int i; 39 const bool operator<(const Query&Qu)const{return r<Qu.r;} 40 }q[maxn]; 41 int main(){ 42 //freopen("hugclose.in","r",stdin); 43 //freopen("hugclose.out","w",stdout); 44 45 scanf("%d%d",&n,&Q); 46 for(int i=1;i<=n;i++)scanf("%d",&a[i]); 47 for(int i=1;i<=Q;i++){ 48 scanf("%d%d%d",&q[i].x,&q[i].l,&q[i].r); 49 q[i].l++;q[i].r++; 50 q[i].i=i; 51 } 52 sort(q+1,q+Q+1); 53 int l=1; 54 for(int i=1;i<=n;i++){ 55 insert(a[i],i); 56 while(q[l].r==i && l<=Q){ 57 b[ q[l].i ]=query(q[l].x,q[l].l); 58 l++; 59 } 60 } 61 for(int i=1;i<=Q;i++)printf("%d\n",b[i]); 62 return 0; 63 }
当然最好的写法就是可持久化了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,q; 4 const int maxn=31*200000+5; 5 int ls[maxn],rs[maxn],sum[maxn]; 6 int root[200005]; 7 int cnt; 8 void modify(int x,int val) 9 { 10 root[x]=++cnt; 11 int now=root[x]; 12 int lst=root[x-1]; 13 for(int i=30;i>=0;i--) 14 { 15 ls[now]=ls[lst]; 16 rs[now]=rs[lst]; 17 sum[now]=sum[lst]+1; 18 if(val&(1<<i)) 19 { 20 rs[now]=++cnt; 21 now=rs[now]; 22 lst=rs[lst]; 23 } 24 else 25 { 26 cnt++; 27 ls[now]=cnt; 28 now=ls[now]; 29 lst=ls[lst]; 30 } 31 } 32 sum[now]=sum[lst]+1; 33 } 34 int query(int x,int l,int r) 35 { 36 //cout<<x<<" "<<l<<" "<<r<<endl; 37 int now=root[r],lst=root[l-1]; 38 int ans=0; 39 for(int i=30;i>=0;i--) 40 { 41 // cout<<now<<" "<<lst<<" "<<sum[now]<<" "<<sum[lst]<<endl; 42 if(x&(1<<i)) 43 { 44 if(sum[ls[now]]==sum[ls[lst]]) 45 { 46 now=rs[now]; 47 lst=rs[lst]; 48 } 49 else 50 { 51 //cout<<i<<endl; 52 ans+=1<<i; 53 now=ls[now]; 54 lst=ls[lst]; 55 } 56 } 57 else 58 { 59 if(sum[rs[now]]==sum[rs[lst]]) 60 { 61 now=ls[now]; 62 lst=ls[lst]; 63 } 64 else 65 { 66 ans+=1<<i; 67 now=rs[now]; 68 lst=rs[lst]; 69 } 70 } 71 } 72 return ans; 73 } 74 int main() 75 { 76 //freopen("hugclose.in","r",stdin); 77 //freopen("hugclose.out","w",stdout); 78 scanf("%d%d",&n,&q); 79 for(int i=1;i<=n;i++) 80 { 81 int x; 82 scanf("%d",&x); 83 modify(i,x); 84 } 85 for(int i=1;i<=q;i++) 86 { 87 int x,l,r; 88 scanf("%d%d%d",&x,&l,&r); 89 l++,r++; 90 printf("%d\n",query(x,l,r)); 91 } 92 }
T8:
非常的巧妙啊,我们发现,当每辆车第一次被拦下的时候,之后的情况都是一样的,所以只需要知道这辆车第一次停下的位置就好了,
同理可以预处理出在每个位置停下的时候到终点的距离,线段树维护就好了
【把验证性问题转化成区间查询问题,可以降低复杂度】
优美的code:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<iostream> 5 #include<cmath> 6 #include<cstdlib> 7 #define LL long long 8 using namespace std; 9 const int maxn=2e5+5; 10 const int INF=1e9; 11 const int maxgr=2e9; 12 int n,q; 13 LL g,r,l[maxn]; 14 int mn[maxn<<5],lson[maxn<<5],rson[maxn<<5],cnt,root; 15 void modify(int &k,int l,int r,int p,int x){ 16 if(!k){ 17 k=++cnt; 18 mn[k]=INF; 19 } 20 mn[k]=min(mn[k],x); 21 if(l==r)return; 22 23 int mid=(1ll*l+1ll*r)>>1; 24 if(p<=mid)modify(lson[k],l,mid,p,x); 25 else modify(rson[k],mid+1,r,p,x); 26 } 27 int query(int k,int l,int r,int x,int y){ 28 if(!k)return INF; 29 30 if(x<=l&&r<=y)return mn[k]; 31 32 int mid=(1ll*l+1ll*r)>>1; 33 int ans=INF; 34 35 if(x<=mid)ans=min(ans,query(lson[k],l,mid,x,y)); 36 if(mid<y)ans=min(ans,query(rson[k],mid+1,r,x,y)); 37 return ans; 38 } 39 LL f[maxn],sum[maxn]; 40 //f[i]表示在i位置停止后(t0=0)后还要多久才能走完 41 inline void STD(){ 42 root=cnt=1; 43 mn[1]=INF; 44 45 for(int i=1;i<=n;i++)sum[i]=sum[i-1]+l[i]; 46 int ql,qr,pos; 47 for(int i=n-1;i;i--){ 48 49 ql=(g+sum[i])%(g+r); 50 qr=(g+r-1+sum[i])%(g+r); 51 if(ql<=qr){ 52 pos=query(root,0,maxgr,ql,qr); 53 } 54 else { 55 pos=min( query(root,0,maxgr,0,qr),query(root,0,maxgr,ql,maxgr) ); 56 } 57 if(pos==INF)f[i]=sum[n]-sum[i]; 58 else{ 59 LL dis=sum[pos]-sum[i]; 60 f[i]=f[pos]+dis+(g+r-dis%(g+r)); 61 } 62 modify(root,0,maxgr,sum[i]%(g+r),i); 63 } 64 scanf("%d",&q); 65 for(int i=1;i<=q;i++){ 66 int t; 67 scanf("%d",&t); 68 ql=((g-t)%(g+r)+(g+r))%(g+r); 69 qr=((g+r-1-t)%(g+r)+(g+r))%(g+r); 70 71 if(ql<=qr)pos=query(root,0,maxgr,ql,qr); 72 else pos=min( query(root,0,maxgr,0,qr),query(root,0,maxgr,ql,maxgr) ); 73 74 if(pos==INF)printf("%lld\n",t+sum[n]); 75 else printf("%lld\n",t+sum[pos]+f[pos]+(g+r-(sum[pos]+t)%(g+r))); 76 } 77 } 78 int main(){ 79 //freopen("wedding.in","r",stdin); 80 //freopen("wedding.out","w",stdout); 81 scanf("%d%lld%lld",&n,&g,&r); 82 n++; 83 for(int i=1;i<=n;i++)scanf("%lld",&l[i]); 84 STD(); 85 return 0; 86 }
——————————【数位DP or 期望DP】——————————
T1:
bzoj3329 Xorequ 【数位DP+Fibonacci】
求满足所有1<=i<=n的i s.t. i ^ (2 * i)==i + (2*i)的i的个数
n<=1e(10^18)
求二进制下没有相邻的1的数的个数
本题写的非常曲折,本来想就是一个数位Dp,但是小于等于n的问题不太好解决,根据原则:不会做DP就再加一维
又添加了一维表示比当前大还是比当前小,后来成功的谢了16个特判A了,但是后来仔细想想,只需要预处理Fibonacci数列,二进制下每次遇到1都加上Fibonacci
就可以很简单的AC了
std:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #define LL long long 6 using namespace std; 7 long long f[100][2][2]; 8 int T; 9 long long n; 10 //[0]比原来小,[1]比原来大 11 //[0]选0,[1]选1 12 int main(){ 13 //freopen("3.out","w",stdout); 14 //scanf("%d",&T); 15 scanf("%d",&T); 16 17 while(T--){ 18 scanf("%lld",&n); 19 20 memset(f,0,sizeof(f)); 21 if(n&1ll){ 22 f[0][0][0]=1ll; 23 f[0][0][1]=0ll; 24 f[0][1][0]=1ll; 25 f[0][1][1]=0ll; 26 } 27 else{ 28 f[0][0][0]=1ll; 29 f[0][0][1]=0ll; 30 f[0][1][0]=0ll; 31 f[0][1][1]=1ll; 32 } 33 LL i; 34 for(i=1ll;(1ll<<i)<=n;i++){ 35 if(!(n&(1ll<<i)) && !(n&(1ll<<(i-1ll) ) ) ){//00 36 //cout<<1; 37 f[i][0][0]=f[i-1][0][0]; 38 f[i][0][1]=f[i-1][1][0]+f[i-1][1][1]+f[i-1][0][1]; 39 f[i][1][0]=0ll; 40 f[i][1][1]=f[i-1][0][1]+f[i-1][0][0]; 41 } 42 else if(!(n&(1ll<<i)) && (n&(1ll<<(i-1ll) ) ) ){//01 43 // cout<<2; 44 f[i][0][0]=f[i-1][1][0]+f[i-1][0][1]+f[i-1][0][0]; 45 f[i][0][1]=f[i-1][1][1]; 46 f[i][1][0]=0ll; 47 f[i][1][1]=f[i-1][0][1]+f[i-1][0][0]; 48 } 49 else if((n&(1ll<<i)) && !(n&(1ll<<(i-1ll) ) ) ){//10 50 //cout<<3; 51 f[i][0][0]=f[i-1][1][0]+f[i-1][1][1]+f[i-1][0][1]+f[i-1][0][0]; 52 f[i][0][1]=0ll; 53 f[i][1][0]=f[i-1][0][0]; 54 f[i][1][1]=f[i-1][0][1]; 55 } 56 else{//11 57 //cout<<4; 58 f[i][0][0]=f[i-1][1][0]+f[i-1][1][1]+f[i-1][0][1]+f[i-1][0][0]; 59 f[i][0][1]=0ll; 60 f[i][1][0]=f[i-1][0][1]+f[i-1][0][0]; 61 f[i][1][1]=0ll; 62 } 63 //printf("less%d %d bigger%d %d\n",f[i][1][0],f[i][0][0],f[i][0][1],f[i][1][1]); 64 } 65 printf("%lld\n",f[i-1][1][0]+f[i-1][0][0]-1ll);//去掉0 66 } 67 return 0; 68 } 69 70 std
T2:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cmath> 6 #include<iostream> 7 using namespace std; 8 const int maxn=1e3+5; 9 char ch[maxn]; 10 int num[15],f[maxn][15],sum[5],n; 11 //f[i][j]表示 任选 i个数,mod 11=j 的方案数 12 inline void Add(int x){num[x]++;for(int i=n;i;i--)for(int j=0;j<11;j++)f[i][(j+x)%11]+=f[i-1][j];} 13 //正向推:因为只能选1次,所以用到的状态(f[i-1][j])必须是不包括x的 14 inline void Del(int x){num[x]--;for(int i=1;i<=n;i++)for(int j=0;j<11;j++)f[i][(j+x)%11]-=f[i-1][j];} 15 //反向退:因为只能删一次,所以用到的状态(f[i-1][j])必须不包括x 16 //又因为之前的状态都包括x,所以反推 17 int main(){ 18 scanf("%s",ch+1); 19 n=strlen(ch+1); 20 f[0][0]=1; 21 for(int i=1;i<=n;i++){ 22 int k=ch[i]-'0'; 23 Add(k); 24 sum[2]=(sum[2]+k)%11; 25 //所有位的和 26 } 27 for(int i=n;i>=1;i--){ 28 for(int j=9;j>=0;j--){ 29 if(!num[j])continue; 30 Del(j); 31 sum[i&1]=(sum[i&1]+j)%11;//奇偶项之和 32 bool fd=0; 33 for(int k=0;k<11;k++){ 34 if(f[i>>1][k] && ((sum[1]+k)<<1)%11==sum[2]){ 35 fd=1; 36 break; 37 } 38 } 39 if(fd){ 40 printf("%d",j); 41 break; 42 } 43 //当然这里也可推出来公式,mod11 下2的逆元是6,所以最后搞一搞就出来了 44 sum[i&1]=(11+sum[i&1]-j)%11; 45 Add(j); 46 } 47 } 48 return 0; 49 }
注:
很多题都是这样子的,例如之前做过的
a1~an的序列,求区间[1,i]内max(xorsum[1,j]+xorsum[j+1,i]) (1<=j<=i)
同样是贪心+Dp搞定的,但是Dp用到了高维前缀和(qzh dalao说bfs也能写)
而且Dp的值并不是我们直接用上的,往往是在某个区间内就可以确定选或不选了
总之,贪心发现很难直观的看出能不能选的时候,可以考虑Dp出某个状态再确定
——————————组合数学【恶心到爆炸】———————————
T1:[排列组合]哲哲的疑惑
题目描述
同同有l个球,球是不同的,现在她要用n种颜色给它们染色,如果一种染色方案有k种颜色没有用到,那么同同会产生C(k,m)的不满意度。
输入描述:
三个数n,m,l
1<=n,m<=10^7,l<=10^18
输出描述:
一个数(对998244353取模),表示所有方案的不满意度之和
示例1
输入
3 2 2
输出
3
说明
有以下方案:
两个球同色,有2种颜色没有用到,同同产生C(2,2)=1的不满意度,然后这里有三种方案,共产生3的不满意度
两个球不同色,有1种颜色没有用到,同同很开心所以总共产生3的不满意度
示例2
输入
1634 1542 130
输出
93812204
说明
无可奉告
分析:
C(k,m)表示k个里面 选m个,其实就等价于在没选的颜色中选m个的方案数
对于每个染色方案,只要能选出m个没染的颜色,就会产生1的贡献,所以最后就等价于推出的式子(还是想想如何转化的问题)
T2:
竟然是组合数学???,好吧确实想不到,但是打表找规律还是可以的,可以用杨辉三角证明
【这里有一种非常好的证明可惜我写不下】
貌似f[i][j]=f[i-1][j]+f[i][j-1] 之类的式子 都和组合数有关吧?
1.数论
1.1 题目描述:
聪明的0v0正在学习莫比乌斯反演。
她看到了这样的一道题:有n*m个人站成了一个n*m的方阵……
剩下的题面,聪明的0v0不记得了。但是,她通过自己高超的数论技巧,给出了一个转化后的模型:
给出n和m,求sigma(1<=i<=n 1<=j<=m)min(n/i,m/j)*[gcd(i,j)==1]
聪明的0v0当然知道怎么做了,但是她想考考你。
1.2输入格式:
一行三个正整数n,m,p。
1.3 输出格式
一行一个非负整数,设答案为x,输出x mod p。
1.4 样例输入
1 2 998244353
1.5 样例输出
2
1.6 样例解释
聪明的0v0不需要样例解释。
1.7 数据范围
30% n,m<=2000 p=998244353。
30% n*m<=10^9 n,m<=10^5 p为质数
20% n,m<=10^6 p为质数
20% n,m<=10^7 p为合数
对于所有数据,保证p<=10^9
...当时真的以为是莫反,结果N*M%P???
其实想一下,两个数之间只有两种关系:
1.互质 即(i,j)==1
那么他会对答案产生1的贡献
2.不互质 即(i,j)>1
设(i,j)=d
那么当计算(i/d,j/d)时就会在min(n/i,m/j)中有(i,j)的贡献
code?没必要吧
2.CRT and EX_CRT
EX_CRT poj2891
推荐blog https://www.cnblogs.com/Miracevin/p/9254795.html
附上自己的板子:
code;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstdlib> 6 #define rep(i,l,r) for(int i=(l);i<=(r);i++) 7 #define LL long long 8 using namespace std; 9 const int maxn=1e6; 10 LL m[maxn],a[maxn]; 11 int n; 12 LL exgcd(LL a,LL b,LL &x,LL &y){ 13 if(!b){ 14 x=1;y=0; 15 return a; 16 } 17 LL ans=exgcd(b,a%b,y,x); 18 y-=(a/b)*x; 19 return ans; 20 } 21 inline LL excrt(){ 22 LL M=m[1],A=a[1],x,y; 23 rep(i,2,n){ 24 LL d=exgcd(M,m[i],x,y); 25 if( (A-a[i])%d )return -1;//exgcd has no solution 26 x=(A-a[i])/d*x %m[i]; 27 A-=x*M; 28 M=M/d*m[i];//maintain the lcm [mi] 29 } 30 return (M+A%M)%M; 31 } 32 int main(){ 33 while(scanf("%d",&n)!=EOF){ 34 rep(i,1,n)scanf("%lld%lld",&m[i],&a[i]); 35 printf("%lld\n",excrt()); 36 } 37 return 0; 38 }
CRT:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define LL long long 3 using namespace std; 4 const int maxn=2e6; 5 LL n,m[maxn],a[maxn],M; 6 LL exgcd(LL a,LL b,LL & x,LL & y){ 7 if(!b){ 8 x=1;y=0; 9 return a; 10 } 11 LL ans=exgcd(b,a%b,y,x); 12 y-=(a/b)*x; 13 return ans; 14 } 15 inline LL CRT(){ 16 LL x,y,ans=0ll; 17 for(int i=1;i<=n;i++){ 18 LL Mi=M/m[i]; 19 exgcd(Mi,m[i],x,y); 20 x=(x%m[i]+m[i])%m[i];//get inv 21 ans=(ans+1ll*a[i]*Mi%M*x%M+M)%M; 22 } 23 return ans; 24 } 25 int main(){ 26 scanf("%lld",&n); 27 M=1ll; 28 for(int i=1;i<=n;i++){ 29 scanf("%lld%lld",&m[i],&a[i]); 30 M*=m[i]; 31 } 32 printf("%lld",CRT()); 33 return 0; 34 }
——————————难题???(sb题)——————————
24点【模拟...???!!!】
当初就没做,直接跳过了后来发现其实也挺好写的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<cstdlib> 5 #include<iostream> 6 #include<algorithm> 7 using namespace std; 8 const int INF=1e9; 9 int num[5]; 10 //+1 -2 *3 /4 11 inline int op(int x,int y,int z){ 12 if(x==INF || y==INF)return INF; 13 14 switch(z){ 15 case 1:{ 16 return x+y; 17 break; 18 } 19 case 2:{ 20 return x-y; 21 break; 22 } 23 case 3:{ 24 return x*y; 25 break; 26 } 27 case 4:{ 28 if(!y)return INF; 29 if(x%y)return INF; 30 else return x/y; 31 break; 32 } 33 } 34 } 35 inline int prio(int opt){return opt>2;}//(1,2)->0 (3,4)->1 36 inline int get_HW(int a,int b,int c,int d){ 37 int HW=INF; 38 for(int i=1;i<=4;i++)//枚举运算符 (a,b) 39 for(int j=1;j<=4;j++)//枚举运算符 (b,c) 40 for(int k=1;k<=4;k++)//枚举运算符 (c,d) 41 { 42 int cnt=0,ans1=0,ans2=0; 43 //ijk 44 ans1=op(a,b,i); 45 if(prio(j)>prio(i) || prio(k)>prio(i)){ 46 cnt++; 47 } 48 ans1=op(ans1,c,j); 49 if(prio(k)>prio(j)){ 50 cnt++; 51 } 52 ans1=op(ans1,d,k); 53 if(ans1==24)HW=min(HW,cnt); 54 55 cnt=0,ans1=0,ans2=0; 56 //ikj 57 ans1=op(a,b,i); 58 if(prio(j)>prio(i) || prio(k)>prio(i)){ 59 cnt++; 60 } 61 ans2=op(c,d,k); 62 if(prio(j)>=prio(k)){ 63 cnt++; 64 } 65 ans1=op(ans1,ans2,j); 66 if(ans1==24)HW=min(HW,cnt); 67 68 cnt=0,ans1=0,ans2=0; 69 //jik 70 ans1=op(b,c,j); 71 if(prio(j)<=prio(i)||prio(k)>prio(j)){ 72 cnt++; 73 } 74 ans1=op(a,ans1,i); 75 if(prio(k)>prio(i)){ 76 cnt++; 77 } 78 ans1=op(ans1,d,k); 79 if(ans1==24)HW=min(HW,cnt); 80 81 cnt=0,ans1=0,ans2=0; 82 //jki 83 ans1=op(b,c,j); 84 if(prio(j)<=prio(i) || prio(k)>prio(j)){ 85 cnt++; 86 } 87 ans1=op(ans1,d,k); 88 if(prio(i)>=prio(k)){ 89 cnt++; 90 } 91 ans1=op(a,ans1,i); 92 if(ans1==24)HW=min(HW,cnt); 93 94 cnt=0,ans1=0,ans2=0; 95 //kij 96 ans1=op(c,d,k); 97 if(prio(j)>=prio(k)||prio(k)<=prio(i)){ 98 cnt++; 99 } 100 ans2=op(a,b,i); 101 if(prio(j)>prio(i)){ 102 cnt++; 103 } 104 ans1=op(ans2,ans1,j); 105 if(ans1==24)HW=min(HW,cnt); 106 107 cnt=0,ans1=0,ans2=0; 108 //kji 109 ans1=op(c,d,k); 110 if(prio(k)<=prio(j)||prio(k)<=prio(i)){ 111 cnt++; 112 } 113 ans1=op(b,ans1,j); 114 if(prio(j)<=prio(i)){ 115 cnt++; 116 } 117 ans1=op(a,ans1,i); 118 if(ans1==24)HW=min(HW,cnt); 119 } 120 return HW; 121 } 122 inline int nxd(int a,int b,int c,int d){ 123 return (a>b)+(a>c)+(a>d)+(b>c)+(b>d)+(c>d); 124 } 125 bool vis[10]; 126 inline bool cf(int a,int b,int c,int d){ 127 memset(vis,0,sizeof(vis)); 128 if(vis[a])return 0; 129 vis[a]=1; 130 if(vis[b])return 0; 131 vis[b]=1; 132 if(vis[c])return 0; 133 vis[c]=1; 134 if(vis[d])return 0; 135 return 1; 136 } 137 int ans; 138 int main(){ 139 scanf("%d%d%d%d",&num[1],&num[2],&num[3],&num[4]); 140 ans=INF; 141 for(int i=1;i<5;i++) 142 for(int j=1;j<5;j++) 143 for(int k=1;k<5;k++) 144 for(int l=1;l<5;l++){ 145 if(cf(i,j,k,l))ans=min(ans,2*nxd(i,j,k,l)+get_HW(num[i],num[j],num[k],num[l])); 146 } 147 if(ans==INF)printf("Mrs.Wanghuo"); 148 else printf("%d",ans); 149 return 0; 150 }
就是枚举的时候有点技巧
1.序列
(array.cpp/c/pas)
【问题描述】
对于一个给定的非负序列{a1,a2,…,an},我们称在1≤i,j≤n时满足k⋅|i−j|≤min(ai,aj)的k为这个序列的扩张指数。
你的任务就是对于某个序列求出它的扩张指数
数据保障一定存在一个扩张指数。
【输入格式】
输入文件名为array.in。
输入的第一行为一个正整数n,代表这个序列元素的个数。
输入的第二行为n个正整数,代表该序列某位置上元素的值。
【输出格式】
输入文件名为array.out。
输入的第一行为一个正整数k,代表这个序列的扩张指数。
【输入输出样例 1】
array.in |
array.out |
4 6 4 5 5 |
1 |
见选手目录下的array/array1.in和array/array1.ans
【输入输出样例 2】
array.in |
array.out |
4 821 500 479 717 |
239 |
见选手目录下的 array/array2.in和 array/array2.ans。
【数据规模与约定】
对于30%的数据满足n≤1000。
对于另外10%的数据满足序列中数字全部相同。
对于100%的数据满足n≤300000。
归程(return)
【题目背景】
在与 Endless 的决战后,std 踏上了归程。
【题目描述】
std 现在在一张 r * c的地图上。他的出发点被标记为 @ ,他的大本营被标
记为 # 。
每秒钟 std 可以向上下左右中的任意一个方向移动一个单位。
地图上有一些水源,用 * 标记,由于std 经过战斗实在是太渴了,他需要去
水源饮水。
地图上还有一些障碍物,用 X 标记,std 不能经过这些障碍物。
其余的地方都为平地,用 . 标记。
他想知道他去饮完水再回到大本营至少需要多少时间,以及他有多少种方案
使得他的总用时最短。
请注意:
1) 即使行走路线相同,饮水处不同也算不同方案。
2) std 可以在找水源的路上经过大本营。
【输入格式】
从文件 return.in 中读入数据。
输入的第一行,为两个正整数r, c,含义见题目描述。
接下来 r 行,每行 c个字符,描述地图的形态。
【输出格式】
输出到 return.out 中。
输出一行,包含两个整数,表示std 所用的最短时间和方案数。
由于方案数可能会非常大,请对方案数取模 1e9+7。
如果没有合法方案,请输出 -1。
【样例 1 输入】
3 3
@ . .
. * .
* . #
【样例 1 输出】
4 5
2.摘Galo(OvO)
2.1 题目描述:
0v0在野外看到了一棵Galo树,看到食物的0v0瞪大了眼睛,变成了OvO。
这棵Galo树可以看做是一棵以1号点为根的n个点的有根数,除了根节点以外,每个节点i都有一个Galo,美味度为w[i]。
OvO发现,如果她摘下了i号Galo,那么i的子树中的Galo以及i到根的路径上的其他Galo都会死掉。
OvO的袋子只能装k个Galo,她的嘴巴里还能叼1个,请问她所摘Galo的美味度之和的最大值是多少?
2.2输入格式:
第一行两个正整数n,k。
第二行到第n行,第i行两个正整数f[i],w[i],表示i号点的父亲为f[i] (保证x[i]<i),第i个Galo美味度为w[i]。
2.3 输出格式
一行一个非负整数,为最大美味值。
2.4 样例输入
4 1
1 10
2 3
2 6
2.5 样例输出
10
2.6 数据范围
30% n,k<=200
30% n*k*k<=10^7
40% n*k<=10^7
对于所有数据,n,k,w[i]<=10^5
2.7 样例解释
尽管OvO最多可以摘两个Galo,但是最优情况是只摘下第二个点的Galo,美味度为10。
数据范围已经给了我们足够的提示了,要么 O(nk^2)70分,要么O(nk)ac
O(nk^2)的比较好写,就是普通的树形背包,但是要注意的是f[i][j]表示以i为根选了j个点(而不是最多选j个,否则就会挂掉)
code(自己写的没保存,借下别人的):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,k; 4 const int maxn=1005; 5 int f[maxn][maxn]; 6 int a[maxn]; 7 const int maxm=1e5+5; 8 int fst[maxm]; 9 int nxt[maxm]; 10 int v[maxm]; 11 int edge; 12 void add(int x,int y) 13 { 14 edge++; 15 nxt[edge]=fst[x]; 16 fst[x]=edge; 17 v[edge]=y; 18 } 19 void dfs(int x) 20 { 21 for(int i=fst[x];i;i=nxt[i]) 22 { 23 dfs(v[i]); 24 for(int j=k;j>=1;j--) 25 { 26 for(int l=1;l<=j;l++) 27 f[x][j]=max(f[x][j],f[x][j-l]+f[v[i]][l]); 28 } 29 } 30 for(int i=1;i<=k;i++) 31 f[x][i]=max(f[x][i],a[x]); 32 } 33 int main() 34 { 35 freopen("b.in","r",stdin); 36 freopen("b.out","w",stdout); 37 scanf("%d%d",&n,&k); 38 k++; 39 for(int i=2;i<=n;i++) 40 { 41 int x; 42 scanf("%d%d",&x,&a[i]); 43 add(x,i); 44 } 45 dfs(1); 46 printf("%d",f[1][k]); 47 }
然后是O(nk)算法,之前看背包九讲就没看懂,现想的
我们想到,之所以会有O(k^2)的转移复杂度,是因为我们把每个节点都当成泛化物品来合并,但是,还有很多浪费的状态,比如一个不怎么样的叶子不可能更新到根节点把
而且我们 想,每个节点对答案的贡献无非就是选或不选,限制条件也就是子树内不选,所以我们这么设计状态:
f[i][k]表示后序遍历前i个点选了k个的最大价值
转移就是f[i][k]=max(f[i-1][k],f[i-size[i]][k-1]+w[i])
code:【建议使用vector,毕竟二维数组转一维数组没怎么用过,代码里有转化公式,转化的时候写错了100->60】
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<cmath> 5 #include<algorithm> 6 #include<cstdlib> 7 #define LL long long 8 using namespace std; 9 const int maxn=2e5+5; 10 const int maxnk=(2e7)+(2e5)+5; 11 int fst[maxn],nxt[maxn],to[maxn],edge_count; 12 inline void add(int x,int y){ 13 edge_count++; 14 to[edge_count]=y; 15 nxt[edge_count]=fst[x]; 16 fst[x]=edge_count; 17 } 18 int size[maxn],dfn[maxn],num[maxn],T; 19 void dfs(int u){ 20 size[u]=1; 21 for(int i=fst[u];i;i=nxt[i]){ 22 int v=to[i]; 23 dfs(v); 24 size[u]+=size[v]; 25 } 26 dfn[u]=++T;//求后序遍历 27 num[T]=u; 28 } 29 int n,k; 30 LL f[maxnk],ans; 31 //f[i][j]表示用了 后序遍历 前i个节点,选了j个,的最大价值 32 inline int trans(int i,int j){return i*(k+1)+j;} 33 //二维转一维的时候要乘 第二维 的大小 34 //公式:(i-i0)*s2+(j-j0)其中i0 j0为下标下界 35 int fa[maxn],w[maxn]; 36 int main(){ 37 freopen("b.in","r",stdin); 38 freopen("b.out","w",stdout); 39 40 scanf("%d%d",&n,&k);k++; 41 42 for(int i=2;i<=n;i++){ 43 scanf("%d%d",&fa[i],&w[i]); 44 add(fa[i],i); 45 } 46 dfs(1); 47 for(int i=1;i<=n;i++){ 48 int u=num[i]; 49 for(int j=1;j<=k;j++){ 50 f[trans(i,j)]=max(f[trans(i-1,j)],f[trans(i-size[u],j-1)]+(LL)w[u]); 51 ans=max(ans,f[trans(i,j)]); 52 } 53 } 54 printf("%lld\n",ans); 55 return 0; 56 }
平时多想想,别到考试了现想,虽然能行但是很险
博弈论。。。
好吧,nim游戏还是会的,只不过这个是阶梯nim,也就是奇数项的nim游戏,因为偶数项最后都要合并到根上,
所以从奇数项拿到偶数项就相当于减掉,从偶数项拿到奇数项就是加上,然后树上问题和链上是一样的,(博弈论真的没学过啊)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 using namespace std; 6 const int maxn=2e5+5; 7 int fst[maxn],nxt[maxn],to[maxn],edge_count; 8 inline void add(int x,int y){ 9 edge_count++; 10 to[edge_count]=y; 11 nxt[edge_count]=fst[x]; 12 fst[x]=edge_count; 13 } 14 int ans,v[maxn]; 15 inline void clear(){ 16 memset(fst,0,sizeof(fst)); 17 memset(nxt,0,sizeof(nxt)); 18 memset(to,0,sizeof(to)); 19 edge_count=0; 20 ans=0; 21 } 22 void dfs(int u,int dep){ 23 if(dep&1)ans^=v[u]; 24 for(int i=fst[u];i;i=nxt[i]){ 25 int v=to[i]; 26 dfs(v,dep+1); 27 } 28 } 29 int t,n; 30 int main(){ 31 //freopen("ore.in","r",stdin); 32 //freopen("ore.out","w",stdout); 33 34 scanf("%d",&t); 35 while(t--){ 36 clear(); 37 scanf("%d",&n); 38 for(int i=1;i<n;i++){ 39 int f; 40 scanf("%d",&f); 41 add(f,i); 42 } 43 for(int i=0;i<n;i++)scanf("%d",&v[i]); 44 dfs(0,0); 45 if(!ans)printf("lose\n"); 46 else printf("win\n"); 47 } 48 return 0; 49 }
【bzoj糖果传递】但是本人没有做过,具体的证明就是,能计算出每个人给其他两个人的能量,假设k=1,其他情况类似,那么就有
xi表示第i个人向左传递的,ci表示第i个人一共多出来的,那么i向右传递的就是ci-xi,然后依次表示,就会得到只和x1有关系的式子,画在数轴上就好了,最后就是求个中位数
no code
——————————大(dark)模拟——————————
T1:
俄罗斯方块(cube)
【题目背景】
std 在玩俄罗斯方块。
【题目描述】
这个游戏可以抽象成共 n个分为4种形态的方块组向下掉落,当方块摞成的
高度大于 ..h时,游戏结束。
为了简化这个问题,我们假设每一排永远不会满,也不会有方块因为该排满
了而被清除。同时,每一个方块组下落的位置都是确定好的,不能左右移动。
方块的形态有:
(1) L 形态:可以被 4个参数描述:x d x1 x2。
x为 L形拐角所处的列。
d为0,1,2,3时,分别表示 L形的4 种朝向:左下、上左、右上、下右。
x1为方块组的竖直高度。
x2为方块组的水平宽度。
(2) T 形态:可以被 5个参数描述:x d x1 x2 x3。
x为T形中心所处的列。
d为0,1,2,3时,分别表示T形的4 种朝向:左下右、上左下、右上左、
下右上。
x1 x2 x3依次表示中心块到 3个端点的长度,顺序与d的描述相同。
(3) Z 形态:可以被 5个参数描述:x d x1 x2 x3。
x为Z形最左侧所处的列。
x1 x2 x3依次表示图中标有 1,2,3的边的长度。
(4) O 形态:可以被 3个参数描述:x r c。
x为矩形方块组的左上角。
r, c为矩形方块的宽度和高度。
问游戏何时结束。
如果在n个方块组掉落之后游戏仍未结束,请求出当前方块堆最高处的高度。
【输入格式】
从文件 cube.in 中读入数据。
输入的第一行为两个正整数 n, h。
接下来 n行,每行有一个方块的信息。
【输出格式】
输出到 cube.out 中。
输出文件共两行。
如果游戏结束了,请在第一行输出 Game Over,并在第二行输出游戏结束时
的时刻(即在第几组方块下落之后游戏结束)。
如果游戏没有结束,请在第一行输出 Game Going on,并在第二行输出当
前方块堆最高处的高度。
【样例 1 输入】
2 10
L 1 3 3 2
T 3 2 3 4 2
【样例 1 输出】
Game Going on
7
【样例 2 输入】
3 5
L 3 1 3 2
Z 2 1 2 3 4
T 3 2 3 2 2
【样例 2 输出】
Game Over
2
【样例 2 解释】
如图。
游戏在 2个方块组掉落后结束。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<algorithm> 5 #include<cmath> 6 #include<iostream> 7 #define LL long long 8 using namespace std; 9 const int maxn=1e5+5; 10 const int INF=1e9; 11 int lson[maxn<<6],rson[maxn<<6],mx[maxn<<6],lazy[maxn<<6],cnt,root; 12 inline void change(int &k,int x){ 13 if(!k)k=++cnt; 14 mx[k]=lazy[k]=x; 15 } 16 inline void down(int k){ 17 if(lazy[k]){ 18 change(lson[k],lazy[k]); 19 change(rson[k],lazy[k]); 20 lazy[k]=0; 21 } 22 } 23 void modify(int &k,int l,int r,int x,int y,int z){//[l,r]->[x,y](z) 24 if(x>y)return; 25 if(!k){ 26 k=++cnt; 27 } 28 //printf("M%d %d %d\n",k,l,r); 29 if(x<=l && r<=y){ 30 change(k,z); 31 } 32 else{ 33 down(k); 34 35 int mid=(l+r)>>1; 36 if(x<=mid)modify(lson[k],l,mid,x,y,z); 37 if(mid<y)modify(rson[k],mid+1,r,x,y,z); 38 mx[k]=max(mx[lson[k]],mx[rson[k]]); 39 } 40 } 41 int query(int k,int l,int r,int x,int y){ 42 if(!k || x>y)return 0; 43 //printf("Q%d %d %d\n",k,l,r); 44 if(x<=l && r<=y)return mx[k]; 45 else{ 46 down(k); 47 48 int ans=0; 49 int mid=(l+r)>>1; 50 if(x<=mid)ans=max(ans,query(lson[k],l,mid,x,y)); 51 if(mid<y)ans=max(ans,query(rson[k],mid+1,r,x,y)); 52 //竟然是挂在了宏定义的使用上。。。不熟练的尽量不要用 53 return ans; 54 } 55 } 56 int n,h; 57 char ch[5]; 58 inline bool L(){ 59 int x,d,x1,x2; 60 scanf("%d%d%d%d",&x,&d,&x1,&x2); 61 switch(d){ 62 case 0:{ 63 int m1=query(1,1,INF,x,x),m2=query(1,1,INF,x-x2+1,x-1); 64 root=1; 65 modify(root,1,INF,x-x2+1,x,max(m1+x1,m2+1)); 66 67 return mx[1]>h; 68 break; 69 } 70 case 1:{ 71 int m2=query(1,1,INF,x-x2+1,x); 72 root=1; 73 modify(root,1,INF,x-x2+1,x-1,m2+1); 74 root=1; 75 modify(root,1,INF,x,x,m2+x1); 76 77 return mx[1]>h; 78 break; 79 } 80 case 2:{ 81 int m2=query(1,1,INF,x,x+x2-1); 82 root=1; 83 modify(root,1,INF,x+1,x+x2-1,m2+1); 84 root=1; 85 modify(root,1,INF,x,x,m2+x1); 86 87 return mx[1]>h; 88 break; 89 } 90 case 3:{ 91 int m1=query(1,1,INF,x,x),m2=query(1,1,INF,x+1,x+x2-1); 92 root=1; 93 modify(root,1,INF,x,x+x2-1,max(m1+x1,m2+1)); 94 95 return mx[1]>h; 96 break; 97 } 98 } 99 } 100 inline bool T(){ 101 int x,d,x1,x2,x3; 102 scanf("%d%d%d%d%d",&x,&d,&x1,&x2,&x3); 103 //printf("T %d %d %d %d %d\n",x,d,x1,x2,x3); 104 switch(d){ 105 case 0:{ 106 int m1=query(1,1,INF,x,x),m2=query(1,1,INF,x-x1+1,x-1),m3=query(1,1,INF,x+1,x+x3-1); 107 root=1; 108 modify(root,1,INF,x-x1+1,x+x3-1,max(m1+x2,max(m2,m3)+1)); 109 110 return mx[1]>h; 111 break; 112 } 113 case 1:{ 114 int m1=query(1,1,INF,x,x),m2=query(1,1,INF,x-x2+1,x-1); 115 root=1; 116 modify(root,1,INF,x-x2+1,x-1,max(m1+x3,m2+1)); 117 root=1; 118 modify(root,1,INF,x,x,max(m1+x3,m2+1)+x1-1); 119 120 return mx[1]>h; 121 break; 122 } 123 case 2:{ 124 int m2=query(1,1,INF,x-x3+1,x+x1-1); 125 root=1; 126 modify(root,1,INF,x-x3+1,x+x1-1,m2+1); 127 root=1; 128 modify(root,1,INF,x,x,m2+x2); 129 130 return mx[1]>h; 131 break; 132 } 133 case 3:{ 134 int m1=query(1,1,INF,x,x),m2=query(1,1,INF,x+1,x+x2-1); 135 root=1; 136 modify(root,1,INF,x,x+x2-1,max(m1+x1,m2+1)); 137 root=1; 138 modify(root,1,INF,x,x,query(1,1,INF,x,x)+x3-1); 139 140 return mx[1]>h; 141 break; 142 } 143 } 144 } 145 inline bool Z(){ 146 int x,d,x1,x2,x3; 147 scanf("%d%d%d%d%d",&x,&d,&x1,&x2,&x3); 148 switch(d){ 149 case 0:{ 150 int m1=query(1,1,INF,x+x2-1,x+x2-1),m2,m3; 151 m2=query(1,1,INF,x,x+x2-2); 152 root=1; 153 modify(root,1,INF,x,x+x2-1,max(m1+x3,m2+1)); 154 root=1; 155 modify(root,1,INF,x,x,query(1,1,INF,x,x)+x1-1); 156 return mx[1]>h; 157 break; 158 } 159 case 1:{ 160 int m1=query(1,1,INF,x,x),m2,m3; 161 m2=query(1,1,INF,x+1,x+x2-1); 162 root=1; 163 modify(root,1,INF,x,x+x2-1,max(m1+x3,m2+1)); 164 root=1; 165 modify(root,1,INF,x+x2-1,x+x2-1,query(1,1,INF,x+x2-1,x+x2-1)+x1-1); 166 167 return mx[1]>h; 168 break; 169 } 170 case 2:{ 171 int m1=query(1,1,INF,x,x+x1-1),m2=query(1,1,INF,x+x1,x+x1+x3-2),m3; 172 root=1; 173 modify(root,1,INF,x,x+x1-2,max(m1+1,m2+2-x2)); 174 root=1; 175 modify(root,1,INF,x+x1-1,x+x1+x3-2,max(m1+x2,m2+1)); 176 177 return mx[1]>h; 178 break; 179 } 180 case 3:{ 181 int m1=query(1,1,INF,x,x+x1-2),m2=query(1,1,INF,x+x1-1,x+x1+x3-2),m3; 182 root=1; 183 modify(root,1,INF,x,x+x1-1,max(m2+x2,m1+1)); 184 root=1; 185 modify(root,1,INF,x+x1,x+x1+x3-2,max(m2+1,m1+2-x2)); 186 187 return mx[1]>h; 188 break; 189 } 190 } 191 } 192 inline bool O(){ 193 int x,r,c; 194 scanf("%d%d%d",&x,&r,&c); 195 root=1; 196 modify(root,1,INF,x,x+r-1,query(1,1,INF,x,x+r-1)+c); 197 198 return mx[1]>h; 199 } 200 int main(){ 201 cnt=1; 202 scanf("%d%d",&n,&h); 203 for(int i=1;i<=n;i++){ 204 scanf("%s",ch); 205 switch(ch[0]){ 206 case'L':{ 207 if(L()){ 208 printf("Game Over\n%d\n",i); 209 return 0; 210 } 211 break; 212 } 213 case'Z':{ 214 if(Z()){ 215 printf("Game Over\n%d\n",i); 216 return 0; 217 } 218 break; 219 } 220 case'T':{ 221 if(T()){ 222 printf("Game Over\n%d\n",i); 223 return 0; 224 } 225 break; 226 } 227 case'O':{ 228 if(O()){ 229 printf("Game Over\n%d\n",i); 230 return 0; 231 } 232 break; 233 } 234 } 235 } 236 printf("Game Going on\n%d\n",mx[1]); 237 return 0; 238 }
code不是很整洁,毕竟是瞎敲的(大模拟还是要打一打的,练手),之后会附上整洁版的
T2:【dfs+模拟+状压】
这道题乍一看还是挺男的,毕竟不能Dp,不能贪心,唯一剩下的w方法就是搜索了,
考虑初始的点越多,显然次数就越少,所以考虑最差的情况 ,一开始只有1个点,
只会有6次,所以我们直接搜索就好了,顺便可以加上一个记忆化,
by rechardluan:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 int t; 5 char s[50]; 6 int len; 7 map<ll,int>mp; 8 int dfs(int x,ll ans) 9 { 10 //if(x==-1)cout<<ans<<"dfd"<<endl; 11 if(x==-1||ans==(1ll<<len)-1)return 0; 12 if(mp.find(ans)!=mp.end())return mp[ans]; 13 if(ans&(1ll<<x))return dfs(x-1,ans); 14 int minn=1<<30; 15 for(int i=1;i<len;i++) 16 if(((ans>>i)|ans)&(1ll<<x)) 17 minn=min(minn,dfs(x-1,(ans>>i)|ans)); 18 return mp[ans]=minn+1; 19 } 20 int main() 21 { 22 //freopen("ash.in","r",stdin); 23 //freopen("ash.out","w",stdout); 24 25 scanf("%d",&t); 26 while(t--) 27 { 28 mp.clear(); 29 int tot=0; 30 ll ans=0; 31 scanf("%s",s); 32 len=strlen(s); 33 for(int i=0;i<len;i++) 34 { 35 if(s[i]=='1')ans+=1ll<<(len-i-1); 36 } 37 if(!(ans&(1ll<<(len-1)))) 38 { 39 puts("-1"); 40 continue; 41 } 42 printf("%d\n",dfs(len-1,ans)); 43 } 44 return 0; 45 }
当然我没想那么多,对于当前状态,第一个空白的点只能被之前的点覆盖,所以我只搜索了所有能覆盖第一个空白点的情况,加上剪枝etc.
【当然本人没有经过严格的证明,所以也没有反例(貌似也造不出来吧)】
by Tj1:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define LL long long 3 using namespace std; 4 const int maxn=45; 5 int t; 6 int n,ans; 7 LL target; 8 char ch[maxn]; 9 inline LL trans(){//字符串转 10 n=strlen(ch+1); 11 target=(1ll<<n)-1ll; 12 13 LL ans=0ll; 14 for(int i=1;i<=n;i++){ 15 if(ch[i]=='1')ans^=(1ll<<(n-i)); 16 } 17 return ans; 18 } 19 void dfs(int pos,int dep,LL now){ 20 if(now==target){ 21 ans=min(ans,dep); 22 } 23 if(dep>=ans)return; 24 25 while(now&(1ll<<(n-pos)))pos++; 26 for(int i=1;i<pos;i++){ 27 if(now^(now>>i))dfs(pos,dep+1,now|(now>>i)); 28 } 29 } 30 int main(){ 31 //freopen("ash.in","r",stdin); 32 //freopen("ash.out","w",stdout); 33 34 scanf("%d",&t); 35 while(t--){ 36 scanf("%s",ch+1); 37 if(ch[1]=='0'){ 38 printf("-1\n"); 39 continue; 40 }//无解 41 ans=strlen(ch+1); 42 dfs(1,0,trans()); 43 printf("%d\n",ans); 44 } 45 return 0; 46 }