集训总结

 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):

 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 }
force

总结:

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.组合数

  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 */
ac Code

...考试时空间炸了,so segment_tree在写的时候尽量不把废物信息(左右端点)存起来,尽量在递归里面写

T2

线段树维护乘积+逆元(注意long long,注意空间别爆了)

 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 }
ac Code

 

T3

线段树+状压:
每次正常修改,用int表示该区域有那些炮弹,区间合并就是  或  运算

 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 }
ac Code

 思考:

当T

——————————基础算法加深——————————

T1:

 

一道思路挺奇特的题,dijkstra算法是广为人知的最短路算法,但是不能求解带负环的,(同样的,乘法也可以,但是不能有除法环)

所以面对乘数极大的情况时,就要使用(double)log10()函数减少储存的数的大小,

 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 }
ac Code

 

3s的时间还是很充裕的,部分分挺水的。

考虑O(nm)算法:
首先的思路是边权为1,我们发现:和最短路经过的起始点不同的次短路正好就是答案

考虑怎么求,显然n遍bfs就可以搞定,但是具体实现的时候有两个注意点:

1.当u的最短路不能更新v时,u的最短路&次短路才能更新v的次短路

2.源点不能再入队,否则就会导致某些节点的次短路为1

 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 */
View Code

no code:看看数据范围就应该能想到这题是个最短路算法,当然肯定是要做些变动的,类比之前的 【newstart 新的开始(点权转边权)】我们能想到建超级源点的做法

本题之所以能建超级源点,就是在于可以有某些点自己走回自己(总代价就剩下了自己的点权),也可以走到别的点

T4:

这题一看就是并查集啊,询问连通性,更关键的是根本没有修改操作

我们维护两种并查集

1.每一行维护一个,表示下次访问跳到哪里

2.整体维护一个,表示是否联通,

最后要注意的是每次修改操作结束后,相邻行列都要扫一下,防止不覆盖造成的不更新(1,1)(1,2)实际上是联通的但是不覆盖

wa code???(敲了2个多小时还没T1 10几分钟写的高):

好吧其实是每个修改操作是一个矩形,可能左上右下,可能右上左下

 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 }
AC? Code

常数问题还需优化...

——————————矩阵加速递推shit题集锦——————————

T1

  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 */
Ac Code

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

于是快速幂就可以过了

 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 Code

(一遍就AC了,开森) 

——————————二分+稍有难度的贪心验证——————————

T1:

一开始想的是虚树+二分,但是数据范围告诉我们虚树没啥用

后来想到了二分不过不幸的是不会验证

下面讲一下这种东西怎么验证:
1.看链:

对于链来说,验证显然贪心就可以,从左到右扫描,尽量放在最右边才能覆盖最多的点

2.进化成树:

我们暂且考虑 结点u 只覆盖子树内的关键结点

由于深度每次增加1,所以u必须放 装置 当且仅当有一个子树内的关键节点距离u为len 否则之后的结点再也不可能覆盖他了

所以贪心是正确的,证毕

Code:

 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 }
AC? Code

 

注意:二分答案往往会有很明显得提示,关键还是如何验证的问题:大多是是贪心,少部分是神奇数据结构,而且确定答案之后的验证多了一“迫不得已”的限制,这就让我们的贪心更加顺利的进行

技巧:

往往有些时候没有强大的数据结构帮我们完成大范围的 修改,查询   这时候考虑从最大or最小值得维护上 出发就会好想一些, 

T2:

apple(???)

首先答案是单调的(可二分的最重要前提)如果直接贪心的话我们不知道 接收器 到底放不放

放->可能会有放中继器更优的解

不放->可能以后就没机会放了

 

但是我们二分的话就可以避免这些问题

如果只放num个,显然 要放前num大的b[i]

而且只有【迫不得已】(引自某题解) 时才放接收器显然是最优的

当然放中继器的话一定可大的来,最后对答案贡献最大

【有个小小的鬼畜错误:check() 里面 if(q==0)break; 就会wa一个点,不加这句话就Ac】

 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 }
View Code

本以为偏差是绝对值,但是没想到竟然不是,所以偏差越大时间越大,二分就好了

no code

——————————数据结构加深——————————

T1:

【bzoj4010】[HNOI2015]菜肴制作 拓扑排序+堆

题目大意:求编号位置序最小(每个编号所在位置最小)

方法1:

建链表,每次把需要放在他前面的放前面,递归去找,最后的表应该就是答案,但是他好像是错的

30·

wa?

希望dalao们想出反例...

。。。艹。。。Rechardluan竟然在几分钟之内就想到反例了

我的算法是基于保证比x小的节点都在x前面,但是问题就在于 每次处理的序列并不一定符合题目要求(不完全等价),比如一组数据是

5 3

5 2

4 2

3 5

12345 -> 1 45 23 -> 14 3 52

但是正确答案应该是

13452

正解:

一个数的位置确定,当且仅当比这个数大的 以及 必须在他后面的 都在他后面(不能把 比他小的放前面,会有问题)

所以我们会想到建反图,每次找最大的 零度节点 删除

最后倒序输出就是答案,完美的避开了之前说的递归区间会有的问题

std:

std

总结:

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]数。

 

  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 }
Ac Code

注:往往区间类问题可以 考虑 合法的区间对答案的贡献 以及等价变形,然后变成一个单点(区间)修改区间查询的问题

T4:

      xiaoqiao

没想到O(nlogn^2)竟然能过:
想到了树状数组+二分查找,但是正解 是平衡树(节操呢?)

最后有一个坑点就是,不保证所有的s<t,所以要进行特判,当s>t时要分裂成两个区间

 

 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 }
AC Code

T5:

1.序列

          (array.cpp/c/pas)

【问题描述】

    对于一个给定的非负序列{a1,a2,…,an},我们称在1i,jn时满足k|ij|min(ai,aj)k为这个序列的扩张指数。

       你的任务就是对于某个序列求出它的扩张指数

       数据保障一定存在一个扩张指数。

【输入格式】

   输入文件名为array.in

输入的第一行为一个正整数n,代表这个序列元素的个数。

输入的第二行为n个正整数,代表该序列某位置上元素的值。

【输出格式】

输入文件名为array.out

输入的第一行为一个正整数k,代表这个序列的扩张指数。

 

【输入输出样例 1

array.in

array.out

 4

 6 4 5 5

1

    见选手目录下的array/array1.inarray/array1.ans   

【输入输出样例 2

array.in

array.out

 4

 821 500 479 717

239

见选手目录下的 array/array2.inarray/array2.ans         

 

【数据规模与约定】

    对于30%的数据满足n1000

   对于另外10%的数据满足序列中数字全部相同。

   对于100%的数据满足n300000

正解写的让人作呕,题解粘上,有能看懂的dalao讲给我听好不好???

【前方高能】

第一题

对于数列 a1,a2,…,an,分析其中任意一项ai,考虑所有的aj >= aiaj < ai的算在aj里面考虑了),则使得min(ai,aj) = ai,要使所有的j都满足k*|i-j| <= min(ai,aj) = ai,则对于ai来说,j应该尽量远离i,这样解出来的最大的k才是有用的(即对aik值的约束最紧)。显然最远的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.     在所有大于等于aiaj中,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值必定有一项是有用的(即对aik值的约束最紧)。

枚举每一项求解出来的有用的k值,再取最小即得到最优答案。

【身后高能】

然后我只会写数据结构优化啊,,,后来发现segtree的常数大约是树状数组的5~10倍左右(血的教训啊啊啊啊啊啊啊啊啊啊啊啊啊啊。。。)

 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 }
AC Code

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??:

 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 } 
??? Code

 T7:

可持久化trie树裸题。。。

暴力的话就不用说了,一种写法是莫队trie树,60`

还有一种写法是离线,记录经过每个节点的数的下标最大是多少

 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 }
ac Code

当然最好的写法就是可持久化了

 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 }
ac Code

T8:

非常的巧妙啊,我们发现,当每辆车第一次被拦下的时候,之后的情况都是一样的,所以只需要知道这辆车第一次停下的位置就好了,

同理可以预处理出在每个位置停下的时候到终点的距离,线段树维护就好了

【把验证性问题转化成区间查询问题,可以降低复杂度】

优美的code:

 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 }
View Code

 

——————————【数位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:

 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
AC Code

T2:

 

 

 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 }
ac Code

注:

很多题都是这样子的,例如之前做过的

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;

 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 }
View Code

CRT:

 

 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 }
View Code

 

——————————难题???(sb题)——————————

24点【模拟...???!!!】

当初就没做,直接跳过了后来发现其实也挺好写的

  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 }
AC Code

就是枚举的时候有点技巧

 

1.序列

(array.cpp/c/pas)

【问题描述】

    对于一个给定的非负序列{a1,a2,…,an},我们称在1i,jn时满足k|ij|min(ai,aj)k为这个序列的扩张指数。

       你的任务就是对于某个序列求出它的扩张指数

       数据保障一定存在一个扩张指数。

【输入格式】

   输入文件名为array.in

输入的第一行为一个正整数n,代表这个序列元素的个数。

输入的第二行为n个正整数,代表该序列某位置上元素的值。

【输出格式】

输入文件名为array.out

输入的第一行为一个正整数k,代表这个序列的扩张指数。

 

【输入输出样例 1

array.in

array.out

 4

 6 4 5 5

1

    见选手目录下的array/array1.inarray/array1.ans   

【输入输出样例 2

array.in

array.out

 4

 821 500 479 717

239

见选手目录下的 array/array2.inarray/array2.ans         

 

【数据规模与约定】

    对于30%的数据满足n1000

   对于另外10%的数据满足序列中数字全部相同。

   对于100%的数据满足n300000

 

      归程(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(自己写的没保存,借下别人的):

 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 }
70` Code

然后是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】

 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 }
ac Code

平时多想想,别到考试了现想,虽然能行但是很险

 

博弈论。。。

好吧,nim游戏还是会的,只不过这个是阶梯nim,也就是奇数项的nim游戏,因为偶数项最后都要合并到根上,

所以从奇数项拿到偶数项就相当于减掉,从偶数项拿到奇数项就是加上,然后树上问题和链上是一样的,(博弈论真的没学过啊)

 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 }
ac Code

【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个方块组掉落后结束。

  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 }
AC? Code

code不是很整洁,毕竟是瞎敲的(大模拟还是要打一打的,练手),之后会附上整洁版的

 T2:【dfs+模拟+状压】

这道题乍一看还是挺男的,毕竟不能Dp,不能贪心,唯一剩下的w方法就是搜索了,

考虑初始的点越多,显然次数就越少,所以考虑最差的情况 ,一开始只有1个点,

只会有6次,所以我们直接搜索就好了,顺便可以加上一个记忆化,

by rechardluan:

 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 }
ac Code

当然我没想那么多,对于当前状态,第一个空白的点只能被之前的点覆盖,所以我只搜索了所有能覆盖第一个空白点的情况,加上剪枝etc.

【当然本人没有经过严格的证明,所以也没有反例(貌似也造不出来吧)】

by Tj1:

 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 }
真ac Code

 

posted @ 2019-09-17 19:39  Tj1  阅读(384)  评论(0编辑  收藏  举报