[51Nod] 51Nod 被虐之路


由于本Cho太过懒惰,不再更新之前已经AC以及个人认为没有很大必要写的题目;

请善用 Ctrl + F qwq

排名先后仅看做题顺序

 

基础题

#1.1 1085 背包问题

01背包模板

 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace std;  4 
 5 int n,maxx,DP[1000000],w,v;  6 
 7 int main(){  8     scanf("%d%d",&n,&maxx);  9     
10     for(int i = 1;i <= n;i++){ 11         scanf("%d%d",&w,&v); 12         for(int j = maxx;j >= w;j--) 13             DP[j] = max(DP[j],DP[j-w]+v); 14  } 15     
16     cout << DP[maxx]; 17     
18     return 0; 19 }
01背包 + 一维表示

#1.2 1086 背包问题 V2

多重背包 + 二进制优化

什么是二进制优化呢?当前物品有数量 c ,将其拆分成 1 2 4 8 16 ... (剩下) ,然后一个个做01背包

决策每一个数字的时候 (1,2,4...) ,选与不选,相当于该物品已有数量加不加当前数字,而我们将决策 c 的二进制表示的每一位,那么所有的 1,2,4,... 是可以组合表示出 ≤c 的所有数量的状态的

这样就优化成一层 log 

 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace std;  4 
 5 int n,maxx,DP[1000000],w,v,c;  6 
 7 int main(){  8     scanf("%d%d",&n,&maxx);  9     
10     for(int i = 1;i <= n;i++){ 11         scanf("%d%d%d",&w,&v,&c); 12         int d = 1; 13         while(d <= c){ 14             for(int j = maxx;j >= d*w;j--) 15                 DP[j] = max(DP[j],DP[j-d*w]+d*v); 16             c -= d; 17             d *= 2; 18         }for(int j = maxx;c && j >= c*w;j--) 19             DP[j] = max(DP[j],DP[j-c*w]+c*v); 20  } 21     
22     cout << DP[maxx]; 23     
24     return 0; 25 }
二进制优化 + 一维表示

#1.3 1257 背包问题 V3

跟分数规划打了场遭遇战= =

数学分析目标式,二分答案

此处解题报告 [51nod] 1257 背包问题 V3

 1 #include<stdio.h>
 2 #include<math.h>
 3 #include<iostream>
 4 #include<algorithm>
 5 using namespace std;  6 
 7 int n,k;  8 long long ansv,answ;  9 
10 double eps = 0.000000001; 11 
12 struct data{ 13     long long w,v; 14     double vrw; 15 }arr[1000000]; 16 
17 bool cmp(const data &a,const data &b){ return a.vrw > b.vrw; } 18 
19 bool check(long long &sumv,long long &sumw,double line){ 20     for(int i = 1;i <= n;i++) 21         arr[i].vrw = 1.0*arr[i].v-line*arr[i].w; 22     
23     sort(arr+1,arr+1+n,cmp); 24     
25     double tmp = 0; 26     sumv = 0,sumw = 0; 27     for(int i = 1;i <= k;i++) 28         sumv += arr[i].v, 29         sumw += arr[i].w, 30         tmp += arr[i].vrw; 31     
32     return tmp >= 0; 33 } 34 
35 long long gcd(long long a,long long b){ 36     if(a < b) swap(a,b); 37     return !b?a:gcd(b,a%b); 38 } 39 
40 int main(){ 41     
42     scanf("%d%d",&n,&k); 43     
44     for(int i = 1;i <= n;i++) 45         scanf("%lld%lld",&arr[i].w,&arr[i].v); 46     
47     double L = 0,R = 1000000; 48     long long sumv,sumw; 49     while(R-L > eps){ 50 // printf("%.8lf %.8lf\n",L,R);
51         double mid = (L+R)/2; 52         if(check(sumv,sumw,mid)) ansv = sumv, answ = sumw, L = mid; 53         else R = mid; 54  } 55     
56     long long d = gcd(ansv,answ); 57     printf("%lld/%lld",ansv/d,answ/d); 58     
59     return 0; 60 }
分数规划

#2.1 1136 欧拉函数

Emmmm,现在有 m 与 p 互质且 p 为质数,那么 phi( p ) = p-1 ,phi( mp ) = phi( m )*phi( p ) = phi( m )*( p-1 )

然后对于 phi( mpp ) = phi( mp )*p

Emmmm 这个我居然没法证明

我们知道如果 phi( mpp ) = phi ( mp )* ??? ,那么 ??? = phi( mpp ) / phi( mp )

而 mpp 和 mp 的质因子是一样的

所以根据公式 phi ( x ) = x*(1-.........) 我们知道对于 x 后面的部分,phi ( mpp ) 和 phi ( mp )是相同的,那么两元相除得 phi ( mpp ) / phi ( mp ) = p

 1 #include<stdio.h>
 2 #include<iostream>
 3 using namespace std;  4 
 5 //int prime[100000],primesize;  6 //bool isprime[100000];  7 //void getlist(int listsize){  8 // listsize = (int)sqrt(listsize)+10;  9 // isprime[ 10 //}
11 
12 int phi(int &x){ 13     int ans = 1; 14     for(int i = 2;i*i <= x;i++){ 15         if(x%i) continue; 16         ans *= (i-1); 17         x /= i; 18         while(x%i == 0){ 19             ans *= i; 20             x /= i; 21  } 22     }if(x > 1) ans *= x-1; // 这条和 i*i <= x 可以防止x为大质数导致TLE
23     return ans; 24 } 25 
26 int main(){ 27     int n; 28     scanf("%d",&n); 29     
30     cout << phi(n); 31     
32     return 0; 33 }
朴素欧拉函数

#3.1 1242 斐波那契数列的第N项

矩阵快速幂

 1 #include<cstdio>
 2 #include<iostream>
 3 #define mod 1000000009
 4 using namespace std;  5 
 6 struct MAT{  7     long long d[5][5];  8     MAT(){ d[1][1] = d[1][2] = d[2][1] = d[2][2] = 0; }  9     void Init(){ 10         d[1][2] = d[2][1] = d[2][2] = 1; 11         d[1][1] = 0; 12  } 13 }I; 14 
15 MAT mul(MAT A,MAT B){ 16  MAT C; 17     for(int i = 1;i <= 2;i++) 18         for(int j = 1;j <= 2;j++) 19             for(int k = 1;k <= 2;k++) 20                 C.d[i][j] = (C.d[i][j]+A.d[i][k]*B.d[k][j]%mod)%mod; 21     return C; 22 } 23 
24 MAT ksm(MAT A,long long k){ 25     MAT B = A; 26     k--; 27     while(k){ 28         if(k&1) B = mul(B,A); 29         A = mul(A,A); 30         k >>= 1; 31     }return B; 32 } 33 
34 int main(){ 35     long long n; 36     scanf("%lld",&n); 37     
38     if(n <= 2) return printf("1"),0; 39     
40  MAT I; 41  I.Init(); 42     
43     I = ksm(I,n-1); 44     
45     long long c = (I.d[1][1]+I.d[1][2])%mod; 46     
47     cout << c; 48     
49     return 0; 50 }
矩阵快速幂

#4 1106 质数检测

Miller-Rabbin 素性测试,二次探测才是核心 

 1 #include<stdio.h>
 2 #include<iostream>
 3 #define LL long long
 4 using namespace std;
 5 
 6 int prime[8] = {2,3,5,7,11,13,17,19};
 7 
 8 LL ksm(LL x,LL k,LL m){
 9     if(k == 1) return x%m;
10     if(!k) return 1;
11     LL b = 1;
12     while(k){
13         if(k&1) b = b*x%m;
14         x = x*x%m;
15         k >>= 1;
16     }return b%m;
17 }
18 
19 bool TD(LL x,LL a){
20     if(x == a) return true;
21     LL m = x-1;
22     while(m % 2 == 0) m >>= 1;
23     LL t = ksm(a,m,x);
24     while(m != x-1 && t != 1 && t != x-1)
25         t = t*t%x,m <<= 1;
26     return (m&1 || t == x-1);
27 }
28 
29 bool isPrime(LL x){
30     if(x == 2) return true;
31     if(x < 1 || x % 2 == 0) return false;
32     for(int i = 0;i < 8;i++)
33         if(!TD(x,prime[i])) return false;
34     return true;
35 }
36 
37 int main(){
38     int n;
39     scanf("%d",&n);
40     
41     for(int i = 1;i <= n;i++){
42         long long x; scanf("%lld",&x);
43         if(isPrime(x)) printf("Yes\n");
44         else printf("No\n");
45     }
46     
47     return 0;
48 }
Miller Rabbin

 #5 1088 最长回文子串

Hash+二分,具体请看这里:

 1 #include<stdio.h>
 2 #include<iostream>
 3 #include<cstring>
 4 #define maxn 421357
 5 #define base 21707
 6 #define ULL unsigned long long
 7 using namespace std;
 8 
 9 ULL stad[maxn],anti[maxn];
10 
11 int n,ans = 1,lens; char s[maxn];
12 
13 ULL Pow(ULL x,int k){
14     if(!k) return 1;
15     if(k == 1) return x;
16     ULL b = 1;
17     while(k){
18         if(k&1) b = b*x;
19         x = x*x;
20         k >>= 1;
21     }return b;
22 }
23 
24 ULL getcode(int L,int R){
25     if(!L) return stad[R];
26     else return stad[R]-stad[L-1]*Pow(base,R-L+1);
27 }
28 
29 ULL getanti(int L,int R){
30     if(R == n) return anti[L];
31     else return anti[L]-anti[R+1]*Pow(base,R-L+1);
32 }
33 
34 int find(int pos){
35     int L = 0,R = min(lens-1-pos,pos),mid;
36     while(L < R){
37         mid = (L+R+1)/2;
38 //        printf("Checking... [%d , %d]\n",pos-mid,pos+mid);
39 //        ULL cntcode = (getanti(pos+1,pos+mid)*base+s[pos])*Pow(base,mid)+getcode(pos+1,pos+mid);
40         if(getcode(pos-mid,pos-1) == getanti(pos+1,pos+mid)) L = mid;
41         else R = mid-1;
42     }return L*2+1;
43 }
44 
45 int exfind(int pos){
46     if(pos == lens-1) return 0;
47     int L = 0,R = min(lens-1-pos,pos+1),mid;
48     while(L < R){
49         mid = (L+R+1)/2;
50 //        printf("Checking... [%d , %d]\n",pos-mid,pos+mid);
51 //        ULL cntcode = (getanti(pos+1,pos+mid)*base+s[pos])*Pow(base,mid)+getcode(pos+1,pos+mid);
52         if(getcode(pos-mid+1,pos) == getanti(pos+1,pos+mid)) L = mid;
53         else R = mid-1;
54     }return L*2;
55 }
56 
57 int main(){
58     scanf("%s",s);
59     lens = strlen(s);
60     
61     ULL cntcode = 0;
62     for(int i = 0;i < lens;i++)
63         cntcode = cntcode*base+s[i],
64         stad[i] = cntcode;
65         
66     cntcode = 0;
67     for(int i = lens-1;i >= 0;i--)
68         cntcode = cntcode*base+s[i],
69         anti[i] = cntcode;
70         
71     for(int pos = 0;pos < lens;pos++)
72         ans = max(ans,find(pos)),
73         ans = max(ans,exfind(pos));
74     cout << ans;
75     
76     return 0;
77 }
Hash+二分

 

3级算法题

#1 1267 4个数和为0

 Hash+加强的check函数

 1 #include<stdio.h>
 2 #include<iostream>
 3 #define ULL unsigned long long
 4 #define maxn 1000007
 5 using namespace std;
 6 
 7 struct data{ ULL val,pos; }sum[maxn];
 8 int m,n,arr[maxn];
 9 
10 struct edge{ ULL val,pos; int from; }e[maxn*4];
11 int tot,first[maxn];
12 
13 bool noequ(ULL A,ULL B){
14     return (A/10000 != B/10000) && (A%10000 != B%10000);
15 }
16 
17 bool check(ULL val,ULL pos){
18     int w = val%maxn;
19     for(int i = first[w];i;i = e[i].from){
20         if(e[i].val == val && noequ(e[i].pos,pos)) return true;
21     }return false;
22 }
23 
24 void insert(int pos1,int pos2,int suuum){
25     if(pos1 > pos2) swap(pos1,pos2);
26     ULL val = (ULL)(suuum+1e9);
27     ULL kcode = pos1*10000+pos2;
28     
29     if(check(val,kcode)) return;
30     
31     int w = val%maxn;
32     
33     tot++; e[tot].from = first[w];
34     e[tot].val = val; e[tot].pos = kcode;
35     first[w] = tot;
36     sum[++m].val = val;
37     sum[m].pos = kcode;
38 }
39 
40 int main(){
41     scanf("%d",&n);
42     
43     for(int i = 1;i <= n;i++)
44         scanf("%d",&arr[i]);
45     
46     for(int i = 1;i <= n;i++)
47         for(int j = i+1;j <= n;j++)
48             insert(i,j,arr[i]+arr[j]);
49     
50 //    for(int i = 1;i <= m;i++) printf("%I64u %I64u\n",sum[i].val,sum[i].pos);
51 //    
52 //    cout << "Hash:" << endl;
53 //    
54 //    for(int i = 1;i <= tot;i++) printf("%I64u %I64u\n",e[i].val,e[i].pos);
55     
56     for(int i = 1;i <= m;i++){
57         if(check(2e9-sum[i].val,sum[i].pos)){
58             cout << "Yes";
59             return 0;
60         }
61     }
62     
63     cout << "No";
64     
65     return 0;
66 }
Hash+补丁

 

4级算法题

#1 1113 矩阵快速幂

蜜汁4级,,,敲一个模板即可过。注意传参的时候推荐使用引用传参而不是直接传参。

 1 #include<stdio.h>
 2 #include<iostream>
 3 #define LL long long
 4 using namespace std;  5 
 6 LL mod = 1000000007;  7 int n,k;  8 
 9 struct mat{ 10     LL d[200][200]; 11 // mat(){ for(int i = 1;i <= n;i++) for(int j = 1;j <= n;j++) d[i][j] = 0; }
12 }; 13 
14 mat mul(mat &A,mat &B){ 15  mat C; 16     for(int i = 1;i <= n;i++){ 17         for(int j = 1;j <= n;j++){ 18             C.d[i][j] = 0; 19             for(int k = 1;k <= n;k++) 20                 C.d[i][j] = (C.d[i][j]+A.d[i][k]*B.d[k][j])%mod; 21  } 22     }return C; 23 } 24 
25 mat ksm(mat &A,int k){ 26 // cout<<k<<endl;
27     if(k == 1) return A; 28 // cout<<k<<endl;
29     mat B = A; k--; 30     while(k){ 31 // cout<<k<<endl;
32         if(k&1) B = mul(B,A); 33         A = mul(A,A); 34         k >>= 1; 35     }return B; 36 } 37 
38 int main(){ 39     scanf("%d%d",&n,&k); 40     
41  mat ans; 42     for(int i = 1;i <= n;i++) 43         for(int j = 1;j <= n;j++) 44             scanf("%lld",&ans.d[i][j]); 45     
46     ans = ksm(ans,k); 47 // return 233;
48     
49     for(int i = 1;i <= n;i++){ 50         for(int j = 1;j <= n;j++) 51             printf("%lld ",ans.d[i][j]%mod); 52         cout << endl; 53  } 54     
55     return 0; 56 }
矩阵快速幂

#2 1051 最大子矩阵和

Emmmmmm 解法比较坑(说明我的基础已经被steam腐蚀殆尽了qwq)

首先给个优化:用前缀和储存子矩阵的和,于是枚举子矩阵的顶行和底行,那么我们就把这个矩阵在计算的时候在某种意义上压成了一行

嘿嘿嘿,然后就是最大字段和了

 1 #include<stdio.h>
 2 #include<iostream>
 3 using namespace std;  4 
 5 int n,m;  6 long long cnt,ans,sum[505][505] = {0};  7 
 8 int main(){  9     scanf("%d%d",&m,&n); 10     
11     for(int i = 1;i <= n;i++){ 12         for(int j = 1;j <= m;j++){ 13             scanf("%lld",&sum[i][j]); 14             sum[i][j] += sum[i-1][j]; 15  } 16  } 17     
18 // for(int i = 1;i <= n;i++){ 19 // for(int j = 1;j <= m;j++) 20 // printf("%lld ",sum[i][j]); 21 // printf("\n"); 22 // }
23     
24     for(int up = 0;up <= n-1;up++){ 25         for(int dw = up+1;dw <= n;dw++){ 26             cnt = 0; 27             for(int i = 1;i <= m;i++){ 28                 cnt = max(cnt+sum[dw][i]-sum[up][i],sum[dw][i]-sum[up][i]); 29                 ans = max(ans,cnt); 30 // if(cnt+(sum[dw][i]-sum[up][i]) < 0){ 31 // ans = max(ans,cnt); cnt = 0; 32 // }else cnt += sum[dw][i]-sum[up][i]; 33 // printf("#[%d-%d]%d: %lld\n",up,dw,i,cnt);
34  } 35 // ans = max(ans,cnt);
36  } 37     }printf("%lld",ans); 38     
39     return 0; 40 }
最大子矩阵和

#3 1287 加农炮

一道标准线段树,,, 由于最近学了一些玄学操作一开始总是想到权值线段树,然后又想到平衡树,,, = =

然而标签二分法已经揭露了一切:只需要一个普通的区间最大值定位和单点修改即可

如果左区间的最大值能够挡下这颗炮弹就朝左边找,反之右边;再反之说明这颗炮弹太高,返回一个异常值即可,然后跳过

数据不坑

 1 #include<stdio.h>
 2 #include<iostream>
 3 #define mid (L+R)/2
 4 #define lc (rt<<1)
 5 #define rc (rt<<1|1)
 6 #define maxn 1000000
 7 using namespace std;  8 
 9 int n,m,connon; 10 
11 struct node{ 12     int maxx; 13 }; 14 
15 struct SegmentTree{ 16     int qL,qR,val,pos; 17     node T[maxn*4]; 18     
19     void build(int rt,int L,int R){ 20         if(L == R){ scanf("%d",&T[rt].maxx); } 21         else{ 22             build(lc,L,mid); build(rc,mid+1,R); 23             T[rt].maxx = max(T[lc].maxx,T[rc].maxx); 24  } 25  } 26     
27     int query(int rt,int L,int R){ 28         if(L == R) return L; 29         else{ 30             if(val <= T[lc].maxx) return query(lc,L,mid); 31             else if(val <= T[rc].maxx) return query(rc,mid+1,R); 32             else return 0; 33  } 34  } 35     
36     void modify(int rt,int L,int R){ 37         if(L == R) T[rt].maxx++; 38         else{ 39             if(pos <= mid) modify(lc,L,mid); 40             else modify(rc,mid+1,R); 41             T[rt].maxx = max(T[lc].maxx,T[rc].maxx); 42  } 43  } 44     
45     void print(int rt,int L,int R){ 46         if(L == R) printf("%d ",T[rt].maxx); 47         else{ print(lc,L,mid); print(rc,mid+1,R); } 48  } 49     
50     void Print(){ print(1,1,n); } 51     
52     void Build(){ build(1,1,n); } 53     
54     int Query(int heigh){ val = heigh; return query(1,1,n); } 55 
56     void Modify(int Emmm){ pos = Emmm; modify(1,1,n); } 57 }A; 58 
59 int main(){ 60     scanf("%d%d",&n,&m); 61     
62  A.Build(); 63     
64     for(int i = 1;i <= m;i++){ 65         scanf("%d",&connon); 66         int pos = A.Query(connon); 67         if(pos <= 1) continue; 68         A.Modify(pos-1); 69  }A.Print(); 70     
71     return 0; 72 }
普通线段树

 #4 1463 找朋友

这道题,跟线段树有什么关系呢??????

正解:离线 + 扫描线

注释:枚举 K 集合,将答案保存至左端点,将右端点排升序,然后计算区间 [ L,R ] 里答案的最大值(此处即可用线段树优化)

这题实在好题,思路之巧妙搞得我都想写题解了

 1 #include<stdio.h>
 2 #include<iostream>
 3 #include<algorithm>
 4 #define mid (L+R)/2
 5 #define lc (rt << 1)
 6 #define rc (rt<<1|1)
 7 #define maxn 1000000
 8 using namespace std;  9 
10 int arr[maxn],brr[maxn],cnt,bos[maxn],sto[maxn],k[maxn],n,m,q; 11 
12 struct node{ 13     int maxx; 14 }T[maxn*4]; 15 
16 void build(int rt,int L,int R){ 17     if(L == R) T[rt].maxx = arr[L]; 18     else{ 19         build(lc,L,mid); build(rc,mid+1,R); 20         T[rt].maxx = max(T[lc].maxx,T[rc].maxx); 21  } 22 } 23 
24 void modify(int rt,int L,int R,int pos,int val){ 25     if(L == R) T[rt].maxx = val,sto[pos] = val; 26     else{ 27         if(pos > R || pos < 1) return; 28         if(pos <= mid) modify(lc,L,mid,pos,val); 29         else modify(rc,mid+1,R,pos,val); 30         T[rt].maxx = max(T[lc].maxx,T[rc].maxx); 31  } 32 } 33 
34 int query(int rt,int L,int R,int qL,int qR){ 35     if(qL <= L && R <= qR) return T[rt].maxx; 36     else{ 37         int ans = 0; 38         if(qL <= mid) ans = max(ans,query(lc,L,mid,qL,qR)); 39         if(qR > mid) ans = max(ans,query(rc,mid+1,R,qL,qR)); 40         return ans; 41  } 42 } 43 
44 struct ask{ 45     int from,L,R,ans,ord; // first[R]
46 }e[maxn]; 47 int tot,first[maxn]; 48 void insert(int L,int R,int ord){ 49     tot++; 50     e[tot].ord = ord, 51     e[tot].L = L, 52     e[tot].R = R, 53     e[tot].from = first[R]; 54     first[R] = tot; 55 } 56 
57 bool cmp(const ask &A,const ask &B){ return A.ord < B.ord; } 58 
59 int main(){ 60     scanf("%d%d%d",&n,&q,&m); 61     
62     for(int i = 1;i <= n;i++) scanf("%d",&arr[i]); 63     for(int i = 1;i <= n;i++) scanf("%d",&brr[i]),bos[brr[i]] = i; 64     for(int i = 1;i <= m;i++) scanf("%d",&k[i]); 65     
66     for(int i = 1;i <= q;i++){ 67         int L,R; 68         scanf("%d%d",&L,&R); 69  insert(L,R,i); 70  } 71     
72 // printf("#bos: "); for(int i = 1;i <= n;i++) printf("%d ",bos[i]); cout << endl;
73     
74     for(int i = 2;i <= n;i++){ 75         for(int j = 1;j <= m;j++){ 76             int ppos = bos[brr[i]-k[j]]; //printf("~#%d: ppos %d i-k[j] %d\n",brr[i],ppos,i-k[j]);
77             if(ppos >= 1 && ppos < i && sto[ppos] < arr[ppos]+arr[i]) 78                 modify(1,1,n,ppos,arr[ppos]+arr[i]); 79             ppos = bos[brr[i]+k[j]]; //printf("~#%d: ppos %d i-k[j] %d\n",brr[i],ppos,i-k[j]);
80             if(ppos >= 1 && ppos < i && sto[ppos] < arr[ppos]+arr[i]) 81                 modify(1,1,n,ppos,arr[ppos]+arr[i]); 82         }for(int p = first[i];p;p = e[p].from){ 83             e[p].ans = query(1,1,n,e[p].L,e[p].R); 84         }//printf("#%d: ",i); for(int j = 1;j <= n;j++) printf("%d ",sto[j]); cout << endl;
85     }sort(e+1,e+1+tot,cmp); 86     
87     for(int i = 1;i <= tot;i++) printf("%d\n",e[i].ans); 88     
89     return 0; 90 }
离线 + 扫描线 + 线段树优化

 

#5 1076 2条不相交的路径

 

第一次写了判桥求边双...

第二次使用Tarjan求强联通的代码强行Ac...

 

 1 #include<stdio.h>
 2 #include<iostream>
 3 #define maxn 2000000
 4 using namespace std;
 5 
 6 int dfn[maxn],low[maxn],TIM,s[maxn],poi,color[maxn],COL,n,m,q;
 7 
 8 struct edge{ int from, u, v; }e[maxn];
 9 int tot,first[maxn]; void insert(int u,int v){
10     tot++; e[tot].from = first[u]; e[tot].v = v; e[tot].u = u; first[u] = tot;
11 }
12 
13 void dfs(int u,int fa){
14 //    printf("Now deep in #%d\n",u);
15     dfn[u] = low[u] = ++TIM;
16     s[poi++] = u;
17     for(int i = first[u];i;i = e[i].from){
18         int v = e[i].v; if(!dfn[v]){
19 //            printf("#%d; get in Poi B\n",u);
20             dfs(v,u); low[u] = min(low[u],low[v]);
21         }else if(dfn[v] < dfn[u] && v != fa) low[u] = min(low[u],dfn[v]);
22     }//printf("#%d: get in poi A\n",u);
23     
24     if(low[u] == dfn[u]){
25 //        printf("#%d get the first!\n",u);
26         color[u] = ++COL;
27         while(s[poi-1] != u && poi){
28             poi--; color[s[poi]] = COL;
29         }if(poi) poi--;
30     }
31 }
32 
33 int main(){
34     scanf("%d%d",&n,&m);
35     
36     for(int i = 1;i <= m;i++){
37         int u,v; scanf("%d%d",&u,&v);
38         insert(u,v); insert(v,u);
39     }for(int i = 1;i <= n;i++)
40         if(!color[i]) dfs(i,i);
41     
42 //    for(int i = 1;i <= n;i++) printf("%d ",color[i]); cout << endl;
43     
44     scanf("%d",&q);
45     
46     for(int i = 1;i <= q;i++){
47         int u,v; scanf("%d%d",&u,&v);
48         if(color[u] == color[v]) printf("Yes\n");
49         else printf("No\n");
50     }
51     
52     
53     
54     
55     return 0;
56 }
Tarjan求强联通

 

 1 #include<stdio.h>
 2 #include<iostream>
 3 #define maxn 1000000
 4 using namespace std;
 5 
 6 int dfn[maxn],low[maxn],TIM,COL,color[maxn],n,m,q;
 7 bool iscut[2*maxn],buck[maxn*2];
 8 
 9 struct edge{ int from,u,v,bri; }e[maxn*2];
10 int tot,first[maxn]; void insert(int u,int v){
11     tot++; e[tot].bri = 0; e[tot].u = u; e[tot].from = first[u]; e[tot].v = v; first[u] = tot;
12 }
13 
14 void dfs(int u,int fa){ // To tag the bri
15     dfn[u] = low[u] = ++TIM;
16     int child = 0;
17     for(int i = first[u];i;i = e[i].from){
18         int v = e[i].v; //if(v == fa) continue;
19         if(v != fa) child++;
20         if(!dfn[v]){
21             dfs(v,u);
22             low[u] = min(low[u],low[v]);
23             if(low[v] >= dfn[u]) iscut[u] = true;
24         }else if(dfn[v] < dfn[u] && v != fa) low[u] = min(low[u],dfn[v]);
25     }if(fa == u && child > 1) iscut[u] = false;
26 //    printf("#%d: child %d iscut %d\n",u,child,iscut[u]);
27 }
28 
29 void tagg(){
30     for(int i = 1;i <= tot;i++)
31         if(iscut[e[i].u] && iscut[e[i].v])
32             e[i].bri = 1, buck[i] = true;
33 }
34 
35 void dfs1(int u,int fa){
36     color[u] = COL;
37     for(int i = first[u];i;i = e[i].from){
38         if(e[i].v == fa || buck[i] || color[e[i].v]) continue;
39         dfs1(e[i].v,u);
40     }
41 }
42 
43 int main(){
44 //    freopen("16.txt","r",stdin);
45 //    freopen("16.out","w",stdout);
46     
47     scanf("%d%d",&n,&m);
48     
49     for(int i = 1;i <= m;i++){
50         int u,v; scanf("%d%d",&u,&v);
51         insert(u,v); insert(v,u);
52     }for(int i=1;i<=n;i++)if(!dfn[i])
53     dfs(i,i); for(int i = 1;i <= n;i++)
54         if(!e[first[i]].from) iscut[i] = true;
55 //    for(int i = 1;i <= n;i++) printf("%d ",iscut[i]); cout << endl;
56     tagg(); 
57     
58 //    for(int i = 1;i <= tot;i++) printf("#%d:%d->%d $%d$\n",i,e[i].u,e[i].v,buck[i]);
59 //    for(int i = 1;i <= n;i++) printf("%d ",color[i]); cout << endl;
60     
61     for(int i = 1;i <= n;i++)
62         if(!color[i]){ ++COL; dfs1(i,i); }
63     
64     scanf("%d",&q);
65     
66     for(int i = 1;i <= q;i++){
67         int u,v; scanf("%d%d",&u,&v);
68         if(color[u] == color[v]) printf("Yes\n");
69         else printf("No\n");
70     }
71     
72     return 0;
73 }
判桥求边双

 

 

5级算法题

#1 1494 选举拉票

Orz 这道题,当真是思考题

具体请看这里:[BZOJ] 1494 选举拉票 #算法设计策略

 1 #include<stdio.h>
 2 #include<queue>
 3 #include<algorithm>
 4 #include<iostream>
 5 #define mid (L+R)/2
 6 #define lc (rt<<1)
 7 #define rc (rt<<1|1)
 8 #define maxn 200000
 9 using namespace std; 10 
11 long long n,list[maxn],qwq; 12 bool vis[maxn]; 13 
14 struct nodd{ 15     long long num,sum; 16 }T[maxn*4]; 17 
18 void modify(long long rt,long long L,long long R,long long pos,long long val){ 19     if(pos < L || pos > R) return; 20     if(L == R) T[rt].num += val,T[rt].sum += val*pos; 21     else{ 22         /*if(pos <= mid)*/ modify(lc,L,mid,pos,val); 23         /*else*/ modify(rc,mid+1,R,pos,val); 24         T[rt].num = T[lc].num+T[rc].num; 25         T[rt].sum = T[lc].sum+T[rc].sum; 26  } 27 } 28 
29 long long query(long long rt,long long L,long long R,long long pos){ 30     if(pos <= 0) return 0; 31     if(pos > T[rt].num) return 2e9; 32     if(L == R) return L*pos;//T[rt].sum;
33     else if(pos <= T[lc].num) return query(lc,L,mid,pos); 34     else return T[lc].sum + query(rc,mid+1,R,pos-T[lc].num); 35 } 36 
37 priority_queue<long long,vector<long long> ,greater<long long> > heap[maxn]; 38 
39 bool cmp1(long long A,long long B){ return heap[A].size() > heap[B].size(); } 40 
41 int main(){ 42     scanf("%lld",&n); 43     
44     for(int i = 1;i <= n;i++){ 45         long long x,y; 46         scanf("%lld%lld",&x,&y); 47  heap[x].push(y); 48         if(x&&!vis[x]) list[++qwq] = x,vis[x] = true; 49         if(x) modify(1,0,20000,y,1); 50  } 51     
52     sort(list+1,list+1+qwq,cmp1); 53 // cout << "list"; 54 // for(int i = 1;i <= qwq;i++) printf("%d ",list[i]); cout << endl;
55     long long s = heap[0].size(),num = 0,sum = 0,ans = 2e9; 56     
57     for(int i = n;i >= max(1LL,s);i--){ 58         for(int j = 1;j <= qwq;j++){ 59             if(heap[list[j]].size() < i) break; 60             while(heap[list[j]].size() >= i){ 61                 sum += heap[list[j]].top(); 62                 modify(1,0,20000,heap[list[j]].top(),-1); 63  heap[list[j]].pop(); 64                 num++; 65  } 66         }ans = min(ans,sum+query(1,0,20000,i-(num+s))); 67 // printf("#%d: ans %d\n",i,ans);
68  } 69     
70     printf("%lld",ans); 71     
72     return 0; 73 }
算法设计策略

#2 1199 Money out of Thin Air

使用线段树维护树的欧拉序

  1 #include<stdio.h>
  2 #include<iostream>
  3 #define mid (L+R)/2
  4 #define lc (rt<<1)
  5 #define rc (rt<<1|1)
  6 #define maxn 1000000
  7 #define LL long long
  8 using namespace std;
  9 
 10 int dfn[maxn],dfl[maxn],TIM,sz[maxn],fa[maxn],n,m;
 11 LL vval[maxn],arr[maxn],ans[maxn];
 12 
 13 struct edge{
 14     int from,v;
 15 }e[maxn]; int tot,first[maxn];
 16 void insert(int u,int v){ tot++; e[tot].from = first[u]; e[tot].v = v; first[u] = tot; }
 17 
 18 void dfs(int u){
 19     dfn[u] = ++TIM;
 20     arr[TIM] = vval[u];
 21     sz[u] = 1;
 22     for(int i = first[u];i;i = e[i].from){
 23         int v = e[i].v;
 24         dfs(v);
 25         sz[u] += sz[v];
 26     }dfl[u] = TIM;
 27 }
 28 
 29 struct node{
 30     LL sum,lazy;
 31 }T[maxn*4];
 32 
 33 void pushdown(int rt,int L,int R){
 34     if(!T[rt].lazy) return;
 35     T[lc].lazy += T[rt].lazy;
 36     T[rc].lazy += T[rt].lazy;
 37     T[lc].sum += T[rt].lazy*(mid-L+1);
 38     T[rc].sum += T[rt].lazy*(R-mid);
 39     T[rt].lazy = 0;
 40 }
 41 
 42 void build(int rt,int L,int R){ // 1,1,TIM
 43     if(L == R) T[rt].sum = arr[L];
 44     else{
 45         build(lc,L,mid); build(rc,mid+1,R);
 46         T[rt].sum = T[lc].sum+T[rc].sum;
 47     }
 48 }
 49 
 50 LL query(int rt,int L,int R,int qL,int qR){
 51     pushdown(rt,L,R);
 52     if(qL <= L && R <= qR) return T[rt].sum;
 53     else{
 54         LL ret = 0;
 55         if(qL <= mid) ret += query(lc,L,mid,qL,qR);
 56         if(qR > mid) ret += query(rc,mid+1,R,qL,qR);
 57         return ret;
 58     }
 59 }
 60 
 61 void modify(int rt,int L,int R,int qL,int qR,LL val){
 62     pushdown(rt,L,R);
 63     if(qL <= L && R <= qR){
 64         T[rt].sum += val*(R-L+1);
 65         T[rt].lazy += val;
 66     }else{
 67         if(qL <= mid) modify(lc,L,mid,qL,qR,val);
 68         if(qR > mid) modify(rc,mid+1,R,qL,qR,val);
 69         T[rt].sum = T[lc].sum + T[rc].sum;
 70     }
 71 }
 72 
 73 void print(int rt,int L,int R){
 74     pushdown(rt,L,R);
 75     if(L == R) ans[L] = T[rt].sum;
 76     else{
 77         print(lc,L,mid);
 78         print(rc,mid+1,R);
 79     }
 80 }
 81 
 82 void final(){
 83     for(int i = 1;i <= n;i++) printf("%lld\n",ans[dfn[i]]);
 84 }
 85 
 86 int main(){
 87 //    freopen("1.in","r",stdin);
 88     
 89     scanf("%d%d",&n,&m);
 90     
 91     for(int i = 2;i <= n;i++){
 92         scanf("%d%lld",&fa[i],&vval[i]);
 93         insert(fa[i]+1,i);
 94 //        insert(fa[i],i);
 95     }
 96     
 97     dfs(1);
 98     build(1,1,TIM);
 99     
100     for(int i = 1;i <= m;i++){
101         char ctr; int x; LL z; double y;
102         cin >> ctr; scanf("%d%lf%lld",&x,&y,&z);
103         
104         x++;
105         if(ctr == 'A'){
106             LL sum = query(1,1,TIM,dfn[x],dfl[x]);
107             if(1.0*sum/(dfl[x]-dfn[x]+1) < y) modify(1,1,TIM,dfn[x],dfl[x],z);
108         }else if(1.0*query(1,1,TIM,dfn[x],dfn[x]) < y) modify(1,1,TIM,dfn[x],dfn[x],z);
109 //        cout << "BBB" << endl;
110     }
111     
112     print(1,1,TIM);
113     final();
114     
115     return 0;
116 }
DFS序+线段树

#3 1378 夹克老爷的愤怒

树形DP

具体请看这里:[51nod] 1378 夹克老爷的愤怒 #树形DP

 1 #include<stdio.h>
 2 #include<iostream>
 3 #define maxn 1000000
 4 using namespace std;
 5 
 6 int DP[maxn][5],buoy[maxn],anch[maxn],depth[maxn],sz[maxn],n,k;
 7 
 8 struct edge{
 9     int from,v;
10 }e[maxn*2]; int tot,first[maxn];
11 void insert(int u,int v){ tot++; e[tot].from = first[u]; e[tot].v = v; first[u] = tot; }
12 
13 void dfs(int u,int pre){
14     int sum = 0; sz[u] = 1;
15     DP[u][0] = DP[u][1] = 0; buoy[u] = 1e9; anch[u] = -1e9;
16     for(int i = first[u];i;i = e[i].from){
17         int v = e[i].v; if(v == pre) continue;
18         depth[v] = depth[u]+1;
19         dfs(v,u);
20         sz[u] += sz[v];
21         buoy[u] = min(buoy[u],buoy[v]);
22         anch[u] = max(anch[u],anch[v]);
23         sum += DP[v][0];
24     }
25     
26     if(sz[u] == 1){
27         DP[u][1] = 1;
28         DP[u][0] = 0;
29         if(!k) DP[u][0] = 1;
30         buoy[u] = anch[u] = depth[u];
31         return;
32     }
33     
34 //    printf("#%d: buoy%d anch%d\n",u,buoy[u],anch[u]);
35     if(anch[u]-depth[u] >= k){ // Must set
36 //        printf("#%d: Poi A\n",u);
37         DP[u][0] = DP[u][1] = sum+1;
38         buoy[u] = depth[u]-k;
39         anch[u] = buoy[u]-1;
40     }else if(2*depth[u]-buoy[u] < anch[u]){ // Can choose
41 //        printf("#%d: Poi B\n",u);
42         DP[u][0] = sum;
43         DP[u][1] = sum+1;
44     }else{ // Needn't
45 //        printf("#%d: Poi C\n",u);
46         DP[u][0] = DP[u][1] = sum;
47         anch[u] = buoy[u]-1;
48     }
49 }
50 
51 int main(){
52     scanf("%d%d",&n,&k);
53     
54     for(int i = 1;i < n;i++){
55         int u,v; scanf("%d%d",&u,&v);
56         u++,v++; insert(u,v); insert(v,u);
57     }depth[1] = 1;
58     
59     dfs(1,1);
60     
61     printf("%d\n",DP[1][1]);
62     
63 //    for(int i = 1;i <= n;i++){
64 //        printf("#%d: 0:%d 1:%d buoy:%d anch:%d\n",i,DP[i][0],DP[i][1],buoy[i],anch[i]);
65 //    }
66     
67     return 0;
68 }
树形DP

 #4 1424 零树

树形DP,这道题是看着题解过的因此并不想写博客

DP[ u ][ 1/0 ]:1和0分别表示到这个点需要增加和减少的操作次数

显然,直接一次操作最值即可

= =

 1 #include<stdio.h>
 2 #include<iostream>
 3 #define maxn 1000000
 4 #define heng(x) (x<0?(-x):x)
 5 using namespace std;
 6 
 7 long long DP[maxn][5],val[maxn],n;
 8 
 9 struct edge{
10     int from,v;
11 }e[maxn*2]; int first[maxn],tot;
12 void insert(int u,int v){ tot++; e[tot].from = first[u]; e[tot].v = v; first[u] = tot; }
13 
14 void dfs(int u,int pre){
15     DP[u][0] = DP[u][1] = 0;
16 //    if(val[u] < 0) DP[u][0] = -val[u],DP[u][1] = 0;
17 //    else DP[u][0] = 0,DP[u][1] = val[u];
18     
19     for(int i = first[u];i;i = e[i].from){
20         int v = e[i].v; if(v == pre) continue;
21         dfs(v,u);
22         DP[u][0] = max(DP[u][0],DP[v][0]);
23         DP[u][1] = max(DP[u][1],DP[v][1]);
24     }int del = DP[u][1]-DP[u][0]+val[u];
25     DP[u][del<0] += heng(del);
26 }
27 
28 int main(){
29     scanf("%lld",&n);
30     for(int i = 1;i < n;i++){
31         int u,v; scanf("%d%d",&u,&v);
32         insert(u,v); insert(v,u);
33     }for(int i = 1;i <= n;i++) scanf("%lld",&val[i]);
34     
35     dfs(1,1);
36     
37     
38     printf("%lld",DP[1][0]+DP[1][1]);
39     
40     return 0;
41 }
树形DP = =

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: