集训总结

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

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

方法1:

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

30·

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<cstdlib>
  4 #include<algorithm>
  5 #include<iostream>
  6 #include<cmath>
  7 #include<queue>
  8 using namespace std;
  9 const int maxn=1e5+5;
 10 int n,m,T;
 11 int fst[maxn],nxt[maxn],to[maxn],edge_count,indegree[maxn];
 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     indegree[y]++;
 18 }
 19 bool vis[maxn];
 20 queue<int>q;
 21 int cnt;
 22 inline bool topological_sort(){
 23     cnt=0;
 24     for(int i=1;i<=n;i++)if(!indegree[i])q.push(i),cnt++;
 25     while(q.size()){
 26         int u=q.front();
 27         q.pop();
 28          
 29         for(int i=fst[u];i;i=nxt[i]){
 30             int v=to[i];
 31             indegree[v]--;
 32             if(!indegree[v])q.push(v),cnt++;
 33         }
 34     }
 35     return cnt==n;
 36 }
 37 int pre[maxn],suf[maxn];
 38 inline void ins(int x,int p){//在p( 数值 )前插入x
 39     int l=pre[p];
 40     pre[x]=l;
 41     suf[l]=x;
 42      
 43     pre[p]=x;
 44     suf[x]=p;
 45      
 46     vis[x]=1;
 47 }
 48 inline void del(int x){//将x从链表中删除
 49     int l=pre[x];
 50     int r=suf[x];
 51     suf[l]=r;
 52     pre[r]=l;
 53 }
 54 inline void init(){
 55     for(int i=0;i<=n+1;i++){
 56         pre[i]=i-1;
 57         suf[i]=i+1;
 58     }
 59 }
 60 int flag[maxn];
 61 int temp[maxn],p;
 62 inline void solve(int x,int y){//传数值
 63     int dep=++cnt;
 64     for(int k=x;pre[k]!=y;k=suf[k])flag[k]=dep;
 65      
 66     for(int k=x;flag[k]==dep;k=suf[k]){
 67         p=0;
 68         vis[k]=1;
 69          
 70         for(int i=fst[k];i;i=nxt[i]){
 71             int v=to[i];
 72             if(!vis[v])temp[++p]=v;
 73         }
 74         if(p){
 75             sort(temp+1,temp+p+1);
 76             for(int i=1;i<=p;i++){
 77                 del(temp[i]);
 78                 ins(temp[i],k);
 79             }
 80             solve(temp[1],temp[p]);
 81         }
 82     }
 83     if(x==1 && y==n){
 84         for(int k=1,ans=1;ans<=n;k=suf[k],ans++){
 85         printf("%d ",k);
 86         }
 87         printf("\n");
 88     }
 89 }
 90 inline void clear(){
 91     memset(indegree,0,sizeof(indegree));
 92     memset(fst,0,sizeof(fst));
 93     memset(nxt,0,sizeof(nxt));
 94     memset(to,0,sizeof(to));
 95     edge_count=0;
 96     memset(vis,0,sizeof(vis));//标记是否处理过
 97     memset(pre,0,sizeof(pre));
 98     memset(suf,0,sizeof(suf));
 99     init();
100 }
101 int main(){
102  
103     scanf("%d",&T);
104     while(T--){
105          
106         scanf("%d%d",&n,&m);
107         clear();
108         for(int i=1,a,b;i<=m;i++){
109             scanf("%d%d",&a,&b);
110             add(b,a);//b前面有a
111         }
112         if(!topological_sort()){printf("impossible\n");continue;}
113         else cnt=0,solve(1,n);
114     }
115     return 0;
116 }
wa?

希望dalao们想出反例...

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

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

5 3

5 2

4 2

3 5

12345 -> 1 45 23 -> 14 3 52

但是正确答案应该是

13452

正解:

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

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

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

std:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<algorithm>
 5 #include<iostream>
 6 #include<cmath>
 7 #include<queue>
 8 using namespace std;
 9 const int maxn=1e5+5;
10 int n,m,T;
11 int fst[maxn],nxt[maxn],to[maxn],edge_count,indegree[maxn];
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     indegree[y]++;
18 }
19 bool vis[maxn];
20 priority_queue<int>q;
21 int ans[maxn];
22 int cnt;
23 inline bool topological_sort(){
24     cnt=0;
25     for(int i=1;i<=n;i++)if(!indegree[i]){
26         q.push(i);
27     }
28     while(q.size()){
29         int u=q.top();
30         ans[++cnt]=u;
31         q.pop();
32          
33         for(int i=fst[u];i;i=nxt[i]){
34             int v=to[i];
35             indegree[v]--;
36             if(!indegree[v])q.push(v);
37         }
38     }
39     return cnt==n;
40 }
41 inline void clear(){
42     memset(indegree,0,sizeof(indegree));
43     memset(fst,0,sizeof(fst));
44     memset(nxt,0,sizeof(nxt));
45     memset(to,0,sizeof(to));
46     edge_count=0;
47 }
48 int main(){
49     scanf("%d",&T);
50     while(T--){
51         scanf("%d%d",&n,&m);
52         clear();
53         for(int i=1,a,b;i<=m;i++){
54             scanf("%d%d",&a,&b);
55             add(b,a);//b前面有a
56         }
57         if(!topological_sort()){printf("impossible\n");continue;}
58         else{
59             for(int i=n;i;i--){
60                 printf("%d ",ans[i]);
61             }
62             printf("\n");
63         }
64     }
65     return 0;
66 }
std

总结:

1.证明算法的复杂度。2.证明算法的正确性(往往贪心算法都需要想反例,从算法的 每一步 去思考,想出可能会造成问题的环节)

 

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

体积巨大的完全背包【数学+背包】

 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

 

一道思路挺奇特的题,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

 

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

 

转载于:https://www.cnblogs.com/a-blog-of-taojiayi-2003/p/11536484.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值