[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 = =

 

posted @ 2017-10-23 13:36  Leviaton  阅读(1167)  评论(0编辑  收藏  举报