应该用线段树写,我是块状链表水过了
#include<map> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int N=320*320; struct Query { char s[5]; int x; }q[N]; int a[N],sz[N],n; ll sum[320][5]; int main() { while(scanf("%d",&n)!=EOF) { int k=0,block=0; while(block*block<n) block++; for(int i=0;i<n;i++) { scanf("%s",q[i].s); if(q[i].s[0]!='s') scanf("%d",&q[i].x),a[k++]=q[i].x; } sort(a,a+k); k=unique(a,a+k)-a; map<int,int>p; for(int i=0;i<k;i++) p[a[i]]=i; memset(a,0,sizeof(a)); memset(sz,0,sizeof(sz)); memset(sum,0,sizeof(sum)); for(int i=0;i<n;i++) { if(q[i].s[0]!='s') { int pos=p[q[i].x],rt=pos/block; int l=rt*block,r=l+block,k=0; a[pos]=q[i].s[0]=='a'?q[i].x:0; memset(sum[rt],0,sizeof(sum[rt])); for(int i=l;i<r;i++) if(a[i]) sum[rt][k++%5]+=a[i]; sz[rt]=k; } else { ll ans=0;k=2; for(int i=0;i<block;i++) ans+=sum[i][k],k=(k-sz[i]+50000)%5; printf("%I64d\n",ans); } } } return 0; }
网络流求最小割,将每个点拆成两个,流量为点权值
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int inf=0x3f3f3f3f; const int N=444,M=88888; FlowNetwork sap; int main() { int n,m,s,d,u,v; while(scanf("%d%d%d%d",&n,&m,&s,&d)!=EOF) { sap.init(); for(int i=1; i<=n; i++) scanf("%d",&v),sap.add(i,i+n,v); while(m--) { scanf("%d%d",&u,&v); sap.add(u+n,v,inf); sap.add(v+n,u,inf); } printf("%d\n",sap.sap(s,n+d,n*2)); } return 0; }
C Counting Formations (HDU 4290)
先求出g(g(g(n)))每一层的循环节
#include<iostream> using namespace std; int main() { long long mod=1000000007,a,b,c,cnt; for(int i=0;i<3;i++) { a=0,b=1,cnt=0; while(1) { c=(3*b+a)%mod; a=b,b=c;cnt++; if(a==0&&b==1) break; } cout<<cnt<<endl; mod=cnt; } return 0; }
记循环节为a=222222224,b=183120,c=240.
则g(g(g(n)))%mod=g(g(g(n%c)%b)%a)%mod,再利用矩阵二分算一下即可:
#include<iostream> #include<cstring> using namespace std; typedef long long ll; struct Mat { int a[2][2]; Mat(){memset(a,0,sizeof(a));} Mat mul(Mat &b,int mod) { Mat c; for(int i=0;i<2;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++) c.a[i][j]=(c.a[i][j]+(ll)a[i][k]*b.a[k][j])%mod; return *this=c; } Mat cal(int n,int mod) { Mat b=*this,c; c.a[0][0]=c.a[1][1]=1; while(n) { if(n&1) c.mul(b,mod); n>>=1; b.mul(b,mod); } return *this=c; } }; int g(int n,int mod) { if(n==0) return 0; Mat a; a.a[0][0]=3;a.a[1][1]=0; a.a[0][1]=a.a[1][0]=1; a.cal(n-1,mod); return a.a[0][0]; } int main() { ll n; while(cin>>n) cout<<g(g(g(n%240,183120),222222224),1000000007)<<endl; return 0; }
网络流,建s,e,将人拆点,从s到food连边,food到人连边,人到人‘连边,人’到drink连边,drink到e连边,求出最大流即可
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int inf=0x3f3f3f3f; const int N=888,M=222222; FlowNetwork sap; char str[N]; int main() { int n,f,d; while(scanf("%d%d%d",&n,&f,&d)!=EOF) { sap.init(); int s=0,e=n*2+f+d+1,x; for(int i=1;i<=f;i++) scanf("%d",&x),sap.add(s,i,x); for(int i=n*2+f+1;i<e;i++) scanf("%d",&x),sap.add(i,e,x); for(int i=f+1;i<=f+n;i++) sap.add(i,i+n,1); for(int i=1;i<=n;i++) { scanf("%s",str); for(int j=0;str[j];j++) if(str[j]=='Y') sap.add(j+1,i+f,inf); } for(int i=1;i<=n;i++) { scanf("%s",str); for(int j=0;str[j];j++) if(str[j]=='Y') sap.add(n+f+i,n*2+f+j+1,inf); } printf("%d\n",sap.sap(s,e,e-s+1)); } return 0; }
相同的区间算一个,区间权值++,但不能超过区间长度。然后就是N个区间,从中选择若干个区间使得权值之和尽可能大。
dp[i]表示选择最后一个区间以i为右端点时能获得的最大权值。
#include<map> #include<cstdio> #include<cstring> using namespace std; typedef pair<int,int>tp; int dp[555]; int main() { int n,a,b; while(scanf("%d",&n)!=EOF) { memset(dp,0,sizeof(dp)); map<tp,int>mp; for(int i=0;i<n;i++) { scanf("%d%d",&a,&b); if(a+b+1<=n&&mp[tp(a+1,n-b)]<n-b-a) mp[tp(a+1,n-b)]++; } for(map<tp,int>::iterator it=mp.begin();it!=mp.end();it++) for(int i=0;i<it->first.first;i++) dp[it->first.second]=max(dp[it->first.second],dp[i]+it->second); int ans=0; for(int i=0;i<=n;i++) ans=max(ans,dp[i]); printf("%d\n",ans); } return 0; }
①由一个数组成,由抽屉原理可知长度一定不超过n,否则会存在长度为a时与长度为b时对n取模结果相同,即循环节为b-a+1,且循环中存在某个数%n==0
②由两个数组成,一定可以。对任意n,则长度1-n且全为1的数中一定存在两个长度i,j的数对n取模相同,现在用较大的数减去较小的数,则结果为111111000000,由两个数组成。
对于每次计算,只需要用pre[k]指向第一次k=x%n对应的x。现在只要保证第一次搜到k时对应的x最小,那么每次从pre[0]一直找到pre[w]=-1,对应就是答案。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=10010; int q[N],pre[N],val[N]; bool vis[N]; char s[N],ss[N]; void bfs(int x,int y,int n,int k) { memset(vis,0,sizeof(vis)); int low=0,up=-1; if(x) q[++up]=x%n,pre[x%n]=-1,val[x%n]=x,vis[x%n]=1; q[++up]=y%n,pre[y%n]=-1,val[y%n]=y,vis[y%n]=1; while(low<=up) { int np=q[low++],tmp; if(np==0) { int w=0,len=strlen(s); for(int k=0;~k;k=pre[k]) ss[w++]=48+val[k]; reverse(ss,ss+w);ss[w]=0; if(!s[0]||len>w||len==w&&strcmp(ss,s)<0) strcpy(s,ss); return; } if(!vis[tmp=(np*k+x)%n]) q[++up]=tmp,pre[tmp]=np,val[tmp]=x,vis[tmp]=1; if(!vis[tmp=(np*k+y)%n]) q[++up]=tmp,pre[tmp]=np,val[tmp]=y,vis[tmp]=1; } return; } int main() { int n,k; while(scanf("%d%d",&n,&k)!=EOF) { s[0]=0; for(int i=1;i<k;i++) bfs(i,i,n,k); if(!s[0]) for(int i=1;i<k;i++) for(int j=0;j<i;j++) bfs(j,i,n,k); puts(s); } return 0; }
H 4 substrings problem (HDU 4295)
考虑相连两层a,b(显然不相连的情况相似):
记tot=w[max(a,b)+1]+……+w[top]:
a在b上面:x=pdv[a]=tot-s[a] y=pdv[b]=tot+w[a]-s[b]
b在a上面:u=pdv[b]=tot-s[b] v=pdv[a]=tot+w[b]-s[a]
①s[a]+w[a]>s[b]+w[b],则有y>v>x与y>u,max(y,x)>max(u,v),所以b在a上面的放法更优。
②s[a]+w[a]>s[b]+w[b],则有v>y>u与v>x,max(y,x)<max(u,v),所以a在b上面的放法更优。
即将k=s[i]+w[i]最大的放在最下面时总PDV最小,而且最优PDV=(w[1]+w[2]+……+w[n])-k。
#include<cstdio> int main() { int n,w,s; while(scanf("%d",&n)!=EOF) { long long k=-1,sum=0; for(int i=0;i<n;i++) scanf("%d%d",&w,&s),k=w+s>k?w+s:k,sum+=w; printf("%I64d\n",sum>k?sum-k:0); } return 0; }
J One and One Story (HDU 4297)