给你一堆1,2,你能够任意排序,要求你输出的数列的前缀和中质数个数最大。ios
发现只有\(2\)是偶质数,那么咱们先放一个\(2\),再放一个\(1\),接下来把\(2\)所有放掉再把\(1\)所有放掉就好了。promise
#include<iostream> #include<cstdio> using namespace std; inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int n,a[3]; int main() { n=read(); for(int i=1;i<=n;++i)a[read()]+=1; if(a[2]) { printf("2 "),a[2]-=1; if(a[1])printf("1 "),a[1]-=1; } while(a[2])printf("2 "),a[2]-=1; while(a[1])printf("1 "),a[1]-=1; puts(""); return 0; }
给你一个串\(S\),会动态的修改三个串,修改是在三个串的末尾删去或加入一个字符。
每次修改完以后回答这三个串可否表示成\(S\)的三个不交的子序列。函数
考虑一个\(dp\),\(f[i][a][b][c]\),表示当前考虑到了串的第\(i\)个位置,匹配了到了三个串的\(a,b,c\)位置。
而后发现第一维这个东西很是蠢。把状态改一下,变成\(f[a][b][c]\)表示三个串分别匹配到\(a,b,c\)时\(i\)的最小值。
这样子单次修改转移的复杂度就是\(O(len^2)\),这样子复杂度就很对了。spa
#include<iostream> #include<cstdio> using namespace std; #define MAX 100100 int n,Q; int nxt[MAX][26],lst[26]; char s[MAX],a[4][MAX]; int len[4],f[255][255][255]; void dp(int x,int y,int z) { if(!(x|y|z))return;f[x][y][z]=n+1; if(x&&f[x-1][y][z]<=n)f[x][y][z]=min(f[x][y][z],nxt[f[x-1][y][z]][a[1][x]-97]); if(y&&f[x][y-1][z]<=n)f[x][y][z]=min(f[x][y][z],nxt[f[x][y-1][z]][a[2][y]-97]); if(z&&f[x][y][z-1]<=n)f[x][y][z]=min(f[x][y][z],nxt[f[x][y][z-1]][a[3][z]-97]); } int main() { scanf("%d%d%s",&n,&Q,s+1); for(int i=0;i<26;++i)lst[i]=n+1; for(int i=n;~i;--i) { for(int j=0;j<26;++j)nxt[i][j]=lst[j]; if(i)lst[s[i]-97]=i; } while(Q--) { char ch[2],ss[2];int x; scanf("%s%d",ch,&x); if(ch[0]=='+') { scanf("%s",ss); a[x][++len[x]]=ss[0]; if(x==1) for(int i=0;i<=len[2];++i) for(int j=0;j<=len[3];++j) dp(len[1],i,j); if(x==2) for(int i=0;i<=len[1];++i) for(int j=0;j<=len[3];++j) dp(i,len[2],j); if(x==3) for(int i=0;i<=len[1];++i) for(int j=0;j<=len[2];++j) dp(i,j,len[3]); } else --len[x]; if(f[len[1]][len[2]][len[3]]<=n)puts("YES");else puts("NO"); } return 0; }
给你一个括号序列,显然一个合法的括号序列和一棵树是对应的。
如今每次交换括号序列的两个位置,求交换完以后的每一棵树的直径。code
首先问题能够变成给定把(
当作\(1\),把)
当作\(-1\)。
因而问题就变成了你要在括号序列上找到任意一段连续的子串,而且把它分红两段,使得后一半的值减去前一半的值最大。
而后线段树维护一下就好了。排序
#include<iostream> #include<cstdio> using namespace std; #define MAX 200200 #define lson (now<<1) #define rson (now<<1|1) inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int n,Q;char s[MAX]; struct Node{int pre[2],suf[2],ans,sum,tot;}t[MAX<<2],L,R; Node operator+(Node a,Node b) { Node c; c.pre[0]=max(a.pre[0],a.sum+b.pre[0]); c.pre[1]=max(a.pre[1],max(a.tot+b.pre[0],-a.sum+b.pre[1])); c.suf[0]=max(b.suf[0],-b.sum+a.suf[0]); c.suf[1]=max(b.suf[1],max(b.tot+a.suf[0],b.sum+a.suf[1])); c.sum=a.sum+b.sum; c.tot=max(a.tot+b.sum,-a.sum+b.tot); c.ans=max(max(a.ans,b.ans),max(a.suf[0]+b.pre[1],a.suf[1]+b.pre[0])); return c; } void Modify(int now,int l,int r,int p) { if(l==r){t[now]=(s[p]=='(')?L:R;return;} int mid=(l+r)>>1; if(p<=mid)Modify(lson,l,mid,p); else Modify(rson,mid+1,r,p); t[now]=t[lson]+t[rson]; } int main() { L=(Node){1,1,0,1,1,1,1};R=(Node){0,1,1,1,1,-1,1}; n=(read()-1)<<1;Q=read();scanf("%s",s+1); for(int i=1;i<=n;++i)Modify(1,1,n,i); printf("%d\n",t[1].ans); while(Q--) { int x,y;swap(s[x=read()],s[y=read()]); Modify(1,1,n,x);Modify(1,1,n,y); printf("%d\n",t[1].ans); } return 0; }
你有一张图,你须要对于每个点回答在任意一棵最小生成树中,\(1\)号点到这个点的路径的最小值是多少。
边权只有两种。three
首先小的边权叫作\(a\),大的边权叫作\(b\)。
如今是\(a\)边构成了一堆联通块,而后你用\(b\)边把这些联通块连接而后求最短路。
这里直接求最短路有一个问题就是你不能在同一个联通块内用\(b\)边,也不能让一条路径重复的通过两次同一个联通块。
那么咱们考虑状压,记录已经访问过了哪一些联通块。
然而这样子是\(2^n\)的。
发现大小为\(1,2,3\)的联通块你不可能用\(b\)边绕一圈再从新进来,因此只须要考虑大小至少为\(4\)的联通块。这样子复杂度就降下来了get
#include<iostream> #include<cstdio> #include<cstring> #include<queue> using namespace std; #define MAX 72 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } struct Line{int v,next,w;}e[500]; int h[MAX],cnt=1; inline void Add(int u,int v,int w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;} int f[MAX],sz[MAX]; int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);} void Merge(int x,int y){x=getf(x);y=getf(y);f[y]=x;sz[x]+=sz[y];} int dis[MAX][131072];bool vis[MAX][131072]; struct Node{int u,d,S;}; bool operator<(Node a,Node b){return a.d>b.d;} priority_queue<Node> Q; void upd(int u,int d,int S){if(dis[u][S]>d)dis[u][S]=d,Q.push((Node){u,d,S});} int n,m,A,B,book[MAX],tim; int main() { n=read();m=read();A=read();B=read(); for(int i=1;i<=n;++i)f[i]=i,sz[i]=1,book[i]=-1; for(int i=1;i<=m;++i) { int u=read(),v=read(),w=read(); Add(u,v,w);Add(v,u,w); if(w==A)Merge(u,v); } for(int i=1;i<=n;++i) if(book[i]==-1&&sz[getf(i)]>3) { for(int j=i;j<=n;++j) if(getf(i)==getf(j)) book[j]=tim; ++tim; } memset(dis,63,sizeof(dis)); upd(1,0,(~book[1])?1<<book[1]:0); while(!Q.empty()) { int u=Q.top().u,S=Q.top().S;Q.pop(); if(vis[u][S])continue;vis[u][S]=true; for(int i=h[u];i;i=e[i].next) { int v=e[i].v,w=e[i].w; if(w==A)upd(v,dis[u][S]+w,S); else { if(getf(u)==getf(v))continue; if(~book[v]&&(S&(1<<book[v])))continue; upd(v,dis[u][S]+w,S|((~book[v])?1<<book[v]:0)); } } } for(int i=1;i<=n;++i) { int ans=1<<30; for(int j=0;j<1<<tim;++j)ans=min(ans,dis[i][j]); printf("%d ",ans); } return 0; }
有一个\(DAG\),如今两我的轮流操做。每次操做能够随意指定一个权值不为\(0\)的点,而后把它的权值减少为任意非负整数,同时能够把其全部出边的点权任意修改。不能操做者输。
求先手是否必胜。generator
首先盲猜确定和\(sg\)函数那套理论相关,而后把每一个点的\(sg\)值给求出来。
题目的结论是:对于全部\(sg=i\)的点,咱们令\(s_k=\oplus_{sg[i]=k}h[i]\),若是存在一个\(s_k\neq 0\)的话那么先手必胜,不然先手必败。string
证实(伪证)
首先\(h\)全是\(0\)的时候必定是先手必败。
对于一个\(sg=k\)的点\(u\),其儿子中一定包含了\([0,k-1]\)这些\(sg\)值。那么咱们找到最大的\(sg\)值,其必定只能影响全部比他小的\(sg\)值的位置。
那么咱们修改这个\(sg\)值中\(h\)最大的那个点,那么一定可让它的\(sg\)值减少,而后必定能够把全部的\(s\)所有变成\(0\),因而咱们变成了一个必败态。
然后手此时操做完以后又必定存在至少一个数是\(0\),又成了必胜态。
#include<iostream> #include<cstdio> #include<vector> using namespace std; #define MAX 200200 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } vector<int> E[MAX]; int n,m,h[MAX],sg[MAX],sum[MAX]; int dg[MAX],Q[MAX],vis[MAX]; void Topsort() { int h=1,t=0; for(int i=1;i<=n;++i)if(!dg[i])Q[++t]=i; while(h<=t){int u=Q[h++];for(int v:E[u])if(!--dg[v])Q[++t]=v;} } int main() { n=read();m=read(); for(int i=1;i<=n;++i)h[i]=read(); for(int i=1;i<=m;++i) { int u=read(),v=read(); E[u].push_back(v); ++dg[v]; } Topsort(); for(int i=n;i;--i) { int u=Q[i]; for(int v:E[u])vis[sg[v]]=i; while(vis[sg[u]]==i)++sg[u]; sum[sg[u]]^=h[u]; } for(int i=n;~i;--i) if(sum[i]) { int pos; for(int j=1;j<=n;++j) if(sg[j]==i&&h[j]>(sum[i]^h[j]))pos=j; h[pos]^=sum[i]; for(int v:E[pos])h[v]^=sum[sg[v]],sum[sg[v]]=0; puts("WIN"); for(int j=1;j<=n;++j)printf("%d ",h[j]); puts("");return 0; } puts("LOSE"); return 0; }