T1
—————————————————————————————————————————————
这道题我是用树形dpA的 $f[i][j]$ 表示包含点i在内的他的子树 内一共有j(j=0或1)个不合法点
x表示当前点 now表示他的某一个儿子
$f[x][1]=min(f[x][1]+f[now][0],f[x][0]+f[now][1],f[x][1]+f[now][1]+e[i].w)$
$f[x][0]=min(f[x][0]+f[now][1],f[x][0]+f[now][0]);$
然后就瞎搞一下就可以A了 注意初始化 不合法点x的$f[x][0]=inf$ 这样才能保证准确性
#include<cstdio> #include<cstring> #include<algorithm> #define LL long long using std::min; const int M=1e5+7; const LL inf=1e18; int read(){ int ans=0,f=1,c=getchar(); while(c<‘0‘||c>‘9‘){if(c==‘-‘) f=-1; c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+(c-‘0‘); c=getchar();} return ans*f; } int n,k,vis[M]; LL f[M][2]; int first[M],cnt; struct node{int to,next,w;}e[2*M]; void ins(int a,int b,int w){e[++cnt]=(node){b,first[a],w}; first[a]=cnt;} void insert(int a,int b,int w){ins(a,b,w); ins(b,a,w);} void dfs(int x){ vis[x]=1; for(int i=first[x];i;i=e[i].next){ int now=e[i].to; if(vis[now]) continue; dfs(now); f[x][1]=min(f[x][0]+f[now][1],f[x][1]+min(f[now][0],f[now][1]+e[i].w)); f[x][0]=f[x][0]+min(f[now][1]+e[i].w,f[now][0]); } } int main(){ int x,y,w; n=read(); k=read(); for(int i=1;i<=k;i++) x=read(),f[x][0]=inf; for(int i=1;i<n;i++) x=read(),y=read(),w=read(),insert(x,y,w); dfs(1); printf("%lld\n",min(f[1][0],f[1][1])); return 0; }
当然也有并查集的写法 就是按边从大到小排序然后如果这条边连接的两个并查集的话
就删掉这条变 删掉边的总和就是答案了
#include<cstdio> #include<cstring> #include<algorithm> #define LL long long using std::min; const int M=1e5+7; const LL inf=1e18; int read(){ int ans=0,f=1,c=getchar(); while(c<‘0‘||c>‘9‘){if(c==‘-‘) f=-1; c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+(c-‘0‘); c=getchar();} return ans*f; } LL ans; int cnt,n,k,f[M],h[M]; int find(int x){while(f[x]!=x) x=f[x]=f[f[x]]; return x;} struct node{int from,to,w;}e[2*M]; bool cmp(node a,node b){return a.w>b.w;} int main(){ int x,y,w; n=read(); k=read(); for(int i=1;i<=n;i++) f[i]=i; for(int i=1;i<=k;i++) x=read(),h[x]=1; for(int i=1;i<n;i++) x=read(),y=read(),w=read(),e[++cnt]=(node){x,y,w}; std::sort(e+1,e+1+n,cmp); for(int i=n;i;i--){ int p=find(e[i].from),q=find(e[i].to); if(h[p]&&h[q]) ans+=e[i].w; else f[q]=p,h[p]+=q; }printf("%lld\n",ans); return 0; }
T2
————————————————————————————————————————
这道题我写的是O(n)的hash判相等 枚举长度判断相等就吼了
#include<cstdio> #include<cstring> #include<algorithm> #define LL long long const int M=2e5+7,P=9875321; unsigned long long h1[M],h2[M],w[M]; int read(){ int ans=0,f=1,c=getchar(); while(c<‘0‘||c>‘9‘){if(c==‘-‘) f=-1; c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+(c-‘0‘); c=getchar();} return ans*f; } char c[26],s1[M],s2[M]; int cnt,ans,wh[26]; bool f=false; int main(){ scanf("%s",c); for(int i=0;i<26;i++) wh[c[i]-‘a‘]=i; scanf("%s",s1+1); cnt=strlen(s1+1); for(int i=1;i<=cnt;i++) s2[i]=c[s1[i]-‘a‘]; w[0]=1; for(int i=1;i<=cnt;i++) w[i]=w[i-1]*P; for(int i=1;i<=cnt;i++) h1[i]=h1[i-1]*P+s1[i]; for(int i=1;i<=cnt;i++) h2[i]=h2[i-1]*P+s2[i]; for(int d=cnt/2;d;d--){ if(h1[d]==h2[cnt]-h2[cnt-d]*w[d]){f=true; ans=d; break;} } if(!f){ printf("%s",(s1+1)); for(int i=1;i<=cnt;i++) printf("%c",‘a‘+wh[s1[i]-‘a‘]); } else{ for(int i=1;i<=cnt-ans;i++) printf("%c",s1[i]); for(int i=1;i<=cnt-ans;i++) printf("%c",‘a‘+wh[s1[i]-‘a‘]); printf("\n"); } return 0; }
——————————————————————————————————
这道题就是类似插头dp的东西 要分一堆类 枚举一个2x2的矩形四条条边的选与不选
一共16(2^4) 种情况 然后就看一下怎么转移 $f[i][j][k]$表示前i行选了j个联通块 当前行中间是否有横杠
#include<cstdio> #include<cstring> #include<algorithm> #define LL long long const int M=2e3+9,mod=1e8+7; int read(){ int ans=0,f=1,c=getchar(); while(c<‘0‘||c>‘9‘){if(c==‘-‘) f=-1; c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+(c-‘0‘); c=getchar();} return ans*f; } int n,m; LL f[M][M][2]; int main(){ n=read(); m=read(); f[1][1][0]=1; f[1][2][1]=1; for(int i=2;i<=n;i++){ // int(*now)[2]=f[i-1]; LL(&last)[M][2]=f[i-1]; for(int k=1;k<=m;k++){ f[i][k][0]=(last[k][0]+2*last[k][1]+last[k-1][0]+last[k-1][1])%mod; f[i][k][1]=(last[k-1][0]*2+last[k][1]+2*last[k-1][1]+last[k-2][0]+last[k-2][1])%mod; } }printf("%lld\n",(f[n][m][0]+f[n][m][1])%mod); return 0; }
时间: 2024-10-05 15:02:56