标签(空格分隔): 未分类html
重作了一遍,原本觉得很快的,结果搞了一天。。。ui
能够发现只有\(\&0\)和\(|1\)会对答案有影响spa
那么对于每一位,咱们只要知道最后一个\(\&1\)和最后一个\(|1\)谁近就能够了。code
发现并很差作,咱们能够把操做串也当成\(01\)串,若是\(\&=0,|=1\)好像并无什么用,因而咱们令\(\&=1,|=0\)发现这样恰好知足了咱们须要的信息,设\(op\)为操做串,这一位串为\(a\),若是\(op\)字典序小于\(a\)最后会是\(1\),不然为\(0\)。(\(|1=01,\&0=10\)这就是字典序了,手玩也能够htm
那么咱们把原串基数排序,那么必定能够重排成\(00...011...1\),不然无解。若是有解那答案就是第一个\(1\)串表明的十进数值减掉最后一个\(0\)串十进制数值。注意下边界条件。blog
\(code\)排序
#include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #define gt getchar() #define ll long long #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) typedef std::pair<int,int> P; #define mk std::make_pair #define fr first #define sc second inline int in() { int k=0;char ch=gt;bool p=1; while(ch<'-')ch=gt;if(ch=='-')ch=gt,p=0; while(ch>'-')k=k*10+ch-'0',ch=gt; return p?k:-k; } const int YL=1e9+7,N=1005,M=5005; inline int ksm(int a,int k){int r=1;while(k){if(k&1)r=1ll*r*a%YL;a=1ll*a*a%YL,k>>=1;}return r;} inline int MO(const int &x){return x>=YL?x-YL:x;} int pw[M],s[N][M],id[2][M],rk[M],res[M]; int main() { int n=in(),m=in(),q=in(); for(int i=1;i<=n;++i) { static char S[M];scanf("%s",S+1); for(int j=1;j<=m;++j)s[i][j]=S[j]-'0'; } for(int i=1;i<=m;++i)id[0][i]=i; int now=0;pw[0]=1; for(int i=1;i<=n;++i) { now^=1;int cnt=0,tot=0;pw[i]=MO(pw[i-1]<<1); for(int j=1;j<=m;++j)cnt+=s[i][j]^1; for(int j=1;j<=m;++j) if(s[i][id[now^1][j]])id[now][++cnt]=id[now^1][j]; else id[now][++tot]=id[now^1][j]; } int *p=id[now]; for(int i=1;i<=m;++i)rk[p[i]]=i; for(int i=1;i<=m;++i) for(int j=1;j<=n;++j) res[i]=MO(res[i]+s[j][i]*pw[j-1]); res[m+1]=pw[n];p[m+1]=m+1; while(q--) { static char S[M];scanf("%s",S+1); int mx=-1,mi=m+1; for(int i=1;i<=m;++i) if(S[i]=='0')mx=std::max(mx,rk[i]); else mi=std::min(mi,rk[i]); if(mx>=mi){puts("0");continue;} printf("%d\n",MO(res[p[mi]]-res[p[mx]]+YL)); } return 0; }
能够发现,走一圈是最优的。游戏
那么即求\(min(max(T_j-j)+i)+n-1\)。ip
楼房重建便可get
\(code\)
#include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #define gt getchar() #define ll long long #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) typedef std::pair<int,int> P; #define mk std::make_pair #define fr first #define sc second inline int in() { int k=0;char ch=gt;bool p=1; while(ch<'-')ch=gt;if(ch=='-')ch=gt,p=0; while(ch>'-')k=k*10+ch-'0',ch=gt; return p?k:-k; } const int N=2e5+5; int mis[N<<2],mip[N<<2],mxv[N<<2],a[N]; #define lc k<<1 #define rc k<<1|1 #define ls l, mid ,lc #define rs mid+1,r,rc #define mid ((l+r)>>1) int calc(int mh,int l,int r,int k) { if(l==r)return std::max(mh,a[l])+l;int w=mxv[rc]; if(mh>=w)return std::min(calc(mh,ls),mh+mid+1); else return std::min(calc(mh,rs),mis[k]); } inline void up(int l,int r,int k) { mxv[k]=std::max(mxv[lc],mxv[rc]); mis[k]=calc(mxv[rc],ls); } void build(int l,int r,int k) { if(l==r)return mxv[k]=a[l],void(); build(ls),build(rs),up(l,r,k); } void upd(int l,int r,int k,int p) { if(l==r)return mxv[k]=a[l],void(); p<=mid?upd(ls,p):upd(rs,p);up(l,r,k); } int main() { int n=in(),m=in(),op=in(),ans=0; for(int i=1;i<=n;++i) a[i]=a[i+n]=in(),a[i]-=i,a[n+i]-=n+i; build(1,n<<1,1);printf("%d\n",ans=mis[1]+n-1); for(int i=1;i<=m;++i) { int x=in()^op*ans,y=in()^op*ans; a[x]=y-x;a[x+n]=y-x-n; upd(1,n<<1,1,x),upd(1,n<<1,1,x+n); printf("%d\n",ans=mis[1]+n-1); } return 0; }
以前的博客是我没理解清写的。
树的\(dp\)是基础,而后把返祖边们抠出来建虚树,枚举两端状况。
这里的\(f[u][0/1]\)是表示\(u\)取\(0/1\)的时候,子树的方案数。
因此咱们只要枚举返祖边的祖先点的状态就能够了。
而后处理转移系数\(xs[u][i=0/1][j=0/1]\)表示虚树上的父亲选\(i\),这个点选\(j\)的系数的转移系数。
注意到边是有影响的,因此不在虚树上的点的\(xs\)表示的是该点选\(i\),这个点子树内第一个虚点选\(j\)的系数,当咱们发现这个\(xs\)转移到一个虚点时,直接把\(xs\)挂在后面那维表明的虚点上。
不在虚树上的点记得乘上这个点选\(0/1\)的方案数。
在虚树上的点的\(xs\)要使得\(xs[u][0][0]=xs[u][1][1]=1\)。
转移的时候若是这个点被强制选了某个值,另外一的\(dp\)初值必须为\(0\)。
\(code\)
#include<vector> #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #define pb push_back #define gt getchar() #define ll long long #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) typedef std::pair<int,int> P; #define mk std::make_pair #define fr first #define sc second inline int in() { int k=0;char ch=gt;bool p=1; while(ch<'-')ch=gt;if(ch=='-')ch=gt,p=0; while(ch>'-')k=k*10+ch-'0',ch=gt; return p?k:-k; } const int YL=998244353,N=1e5+5;typedef std::vector<int> vi; inline int ksm(int a,int k){int r=1;while(k){if(k&1)r=1ll*r*a%YL;a=1ll*a*a%YL,k>>=1;}return r;} inline int MO(const int &x){return x>=YL?x-YL:x;} vi G[N],E[N];int xs[N][2][2],f[N][2],g[N][2],tsz[N],imp[N],o[N]; int Eu[N],Ev[N],tot,dep[N],fg[N][2],vis[N],tt; void pre_dfs(int u,int pa=0) { o[u]=++tt; for(int v:G[u])if(v==pa)continue; else if(!o[v])pre_dfs(v,u),tsz[u]+=tsz[v]; else { imp[u]=1; if(o[u]<o[v]) Eu[++tot]=u,Ev[tot]=v; } imp[u]|=tsz[u]>=2;tsz[u]=tsz[u]||imp[u]; } void mul(int f[2][2],int g[2][2]) { int f00=f[0][0],f01=f[0][1]; int f10=f[1][0],f11=f[1][1]; g[0][0]=MO(f00+f10); g[0][1]=MO(f01+f11); g[1][0]=f00,g[1][1]=f01; } inline void init(int f[2][2]){f[0][0]=f[1][1]=1,f[0][1]=f[1][0]=0;} int dfs(int u) { vis[u]=g[u][0]=g[u][1]=1;int pos=0; for(int v:G[u]) if(!vis[v]) { int w=dfs(v); if(!w) { g[u][0]=1ll*g[u][0]*(g[v][1]+g[v][0])%YL; g[u][1]=1ll*g[u][1]*g[v][0]%YL; } else if(!imp[u])mul(xs[v],xs[u]),pos=w; else mul(xs[v],xs[w]),E[u].pb(w),pos=w; } if(imp[u])return init(xs[u]),u; xs[u][0][0]=1ll*xs[u][0][0]*g[u][0]%YL; xs[u][0][1]=1ll*xs[u][0][1]*g[u][0]%YL; xs[u][1][0]=1ll*xs[u][1][0]*g[u][1]%YL; xs[u][1][1]=1ll*xs[u][1][1]*g[u][1]%YL; return pos; } void dp(int u) { f[u][0]=fg[u][1]?0:g[u][0]; f[u][1]=fg[u][0]?0:g[u][1]; for(int v:E[u]) { dp(v); for(int i=0;i<2;++i) f[u][i]=(1ll*xs[v][i][0]*f[v][0]+1ll*xs[v][i][1]*f[v][1])%YL*f[u][i]%YL; } } int main() { int n=in(),m=in(),ans=0; for(int i=1,u,v;i<=m;++i) u=in(),v=in(),G[u].pb(v),G[v].pb(u); pre_dfs(1),imp[1]=1,dfs(1);int mx=1<<tot; for(int i=0;i<mx;++i) { for(int j=0;j<tot;++j) if(i>>j&1)fg[Eu[j+1]][1]=1,fg[Ev[j+1]][0]=1; else fg[Eu[j+1]][0]=1; dp(1);ans=MO(ans+MO(f[1][1]+f[1][0])); for(int j=0;j<tot;++j) if(i>>j&1)fg[Eu[j+1]][1]=0,fg[Ev[j+1]][0]=0; else fg[Eu[j+1]][0]=0; } printf("%d\n",ans); return 0; }
咱们发现若是一个点能到\(L\),且另外一个点能到它,那么另外一个点确定能到\(L\),因此咱们预处理\(L[i],R[i]\)为\(i\)点能扩大的最大范围。咱们须要安排一个顺序使得他们最优。
若是一扇门\(x,x+1\)的钥匙在\(1-x\)则确定要先转移\(x+1\)再转移\(x\),反之亦然。
因而咱们能够拓扑排序。
问题的关键在于门是不满的,因此有不少没有关系的点之间跳来跳去,复杂度就假了。
可是因为数据只卡了正着作的,没卡反着作的,因而他的乱搞就能过(他写了两篇乱搞(小声。
那咱们用并查集把没有门的点缩起来就\(ok\)了。
\(code\)
#include<queue> #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #define gt getchar() #define ll long long #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) typedef std::pair<int,int> P; #define mk std::make_pair #define fr first #define sc second inline int in() { int k=0;char ch=gt;bool p=1; while(ch<'-')ch=gt;if(ch=='-')ch=gt,p=0; while(ch>'-')k=k*10+ch-'0',ch=gt; return p?k:-k; } const int N=1e6+5; int head[N],to[N],du[N],p[N],L[N],R[N],nxt[N],cnt,key[N],tot,n,fa[N]; inline void add(int u,int v){to[++cnt]=v,nxt[cnt]=head[u],head[u]=cnt,++du[v];} int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} inline void work(int u) { int l=L[u],r=R[u],nl,nr; while(1) { nl=l,nr=r; while(l>1&&(!key[l-1]||(l<=key[l-1]&&key[l-1]<=r)))l=L[find(l-1)]; while(r<n&&(!key[ r ]||(l<=key[ r ]&&key[ r ]<=r)))r=R[find(r+1)]; if(nl==l&&nr==r)break; } L[u]=l,R[u]=r; } int main() { n=in();int m=in(),q=in(); for(int i=1,x,y;i<=m;++i)x=in(),y=in(),key[x]=y; for(int i=1;i<=n;++i)fa[i]=i; for(int i=1;i<n;++i)if(!key[i])fa[i+1]=find(i); for(int i=1;i<n;++i) if(key[i]) { if(key[i]<=i)add(find(i+1),find(i)); else add(find(i),find(i+1)); } std::queue<int>Q; for(int i=1;i<=n;++i)if(fa[i]==i&&!du[i])Q.push(i); while(!Q.empty()) { int u=p[++tot]=Q.front();Q.pop(); for(int i=head[u];i;i=nxt[i]) if(!--du[to[i]])Q.push(to[i]); } for(int i=1;i<=n;++i)R[find(i)]=i; for(int i=n;i>=1;--i)L[find(i)]=i; for(int i=1;i<=tot;++i)work(p[i]); while(q--){int x=find(in()),y=find(in());puts(L[x]<=y&&y<=R[x]?"YES":"NO");} return 0; }
先把依赖关系的\(DAG\)建出来。
而后就是贪心,小的必定要尽可能放在前面。
考虑当前最小值,它在父亲节点删掉后必定会被删。
因此能够并起来,而后咱们如今是考虑一堆序列的顺序,推下式子发现只要平均值小就必定先选,就没了。
\(code\)
#include<cstdlib> #include<vector> #include<queue> #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #define gt getchar() #define ll long long #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) #define mk std::make_pair #define fr first #define sc second #define double long double typedef std::pair<double,int> P; inline int in() { int k=0;char ch=gt;bool p=1; while(ch<'-')ch=gt;if(ch=='-')ch=gt,p=0; while(ch>'-')k=k*10+ch-'0',ch=gt; return p?k:-k; } typedef std::vector<int> vi; const int N=5e5+5;vi G[N]; const double eps=1e-6; int o[N],sz[N],fa[N],ff[N];ll w[N]; struct Queue { std::priority_queue<P>Q1,Q2; void push(P x){Q1.push(x);} void erase(P x){Q2.push(x);} void upd(){while(!Q2.empty()&&Q1.top()==Q2.top())Q1.pop(),Q2.pop();} inline void pop(){upd();Q1.pop();} inline P top(){upd();return Q1.top();} }Q; int dfs(int u) { int ans=u!=0;o[u]=1; for(int v:G[u]) if(o[v])puts("-1"),exit(0); else ans+=dfs(v);return ans; } int find(int x){return x==ff[x]?x:ff[x]=find(ff[x]);} int main() { int n=in();ll ans=0; for(int i=1;i<=n;++i)G[fa[i]=in()].push_back(i); for(int i=1;i<=n;++i)ans+=w[i]=in(); if(dfs(0)!=n)return puts("-1"),0; for(int i=1;i<=n;++i)ff[i]=i,sz[i]=1; for(int i=1;i<=n;++i)Q.push(mk(-(double)w[i],i)); for(int i=1;i<=n;++i) { P now=Q.top();Q.pop();int u=find(now.sc),v=find(fa[u]); if(v)Q.erase(mk(-(double)w[v]/sz[v],v)); ans+=w[u]*sz[v],w[v]+=w[u],sz[v]+=sz[u],ff[u]=v; if(v)Q.push(mk(-(double)w[v]/sz[v],v)); } printf("%lld\n",ans); return 0; }
普及\(dp\),设\(F[i][j][k]\)表示第\(i\)个城市,没修的公路有\(j\)条,没修的铁路有\(k\)条的最小代价。
叶子节点直接算,非叶子节点枚举修什么。考场上好像卡空间,用分治的\(fft\)的卡空间技巧就好了
\(code\)
#include<vector> #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #define gt getchar() #define ll long long #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) typedef std::pair<int,int> P; #define mk std::make_pair #define fr first #define sc second inline int in() { int k=0;char ch=gt;bool p=1; while(ch<'-')ch=gt;if(ch=='-')ch=gt,p=0; while(ch>'-')k=k*10+ch-'0',ch=gt; return p?k:-k; } typedef std::vector<int> vi; const int N=20005;vi G[N]; ll f[N][41][41],a[N],b[N],c[N]; ll dfs(int u,int L,int R) { if(u<0)return c[-u]*(a[-u]+L)*(b[-u]+R); if(~f[u][L][R])return f[u][L][R];int lc=G[u][0],rc=G[u][1]; return f[u][L][R]=std::min(dfs(lc,L+1,R)+dfs(rc,L,R),dfs(lc,L,R)+dfs(rc,L,R+1)); } int main() { int n=in(); for(int i=1;i<n;++i) { int s=in(),t=in(); G[i].push_back(s),G[i].push_back(t); } for(int i=1;i<=n;++i)a[i]=in(),b[i]=in(),c[i]=in(); memset(f,-1,sizeof f);printf("%lld\n",dfs(1,0,0)); return 0; }