Day1:
T1:
直接模拟就行。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=100;
int n,ans[N][N];
struct S{int x,y;}a[N*N];
int main(){
int i,j,x,y;
scanf("%d",&n);
ans[1][n/2+1]=1;
a[1].x=1;a[1].y=n/2+1;
for(i=2;i<=n*n;++i){
x=a[i-1].x;y=a[i-1].y;
if(x==1&&y!=n){
ans[n][y+1]=i;
a[i].x=n;a[i].y=y+1;
}
else if(x!=1&&y==n){
ans[x-1][1]=i;
a[i].x=x-1;a[i].y=1;
}
else if(x==1&&y==n){
ans[x+1][y]=i;
a[i].x=x+1;a[i].y=y;
}
else{
if(x-1>0&&y+1<=n&&!ans[x-1][y+1]){
ans[x-1][y+1]=i;
a[i].x=x-1;a[i].y=y+1;
}
else{
ans[x+1][y]=i;
a[i].x=x+1;a[i].y=y;
}
}
}
for(i=1;i<=n;++i){
for(j=1;j<=n;++j)
printf("%d ",ans[i][j]);
printf("\n");
}
return 0;
}
T2:
题目是让求出一个最小环,因为是有向图而且每个点只有一条出边,所以直接dfs就行。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=200010;
bool check;
struct S{int st,en;}aa[N*10];
int n,a[N],tot,ans,point[N],next[N*10],f[N],use[N];
inline int in(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void add(int x,int y){
tot+=1;next[tot]=point[x];point[x]=tot;
aa[tot].st=x;aa[tot].en=y;
}
inline void find(int x,int y,int z){
int i;
if(check) return ;
f[x]=y;use[x]=z;
for(i=point[x];i;i=next[i]){
if(use[aa[i].en]==0) find(aa[i].en,y+1,z);
else if(use[aa[i].en]==z){
ans=min(ans,y-f[aa[i].en]+1);
check=true;
return ;
}
}
}
int main(){
int i,num=0;
n=in();
for(i=1;i<=n;++i) a[i]=in(),add(i,a[i]);
ans=0x7fffffff;
for(i=1;i<=n;++i)
if(!use[i]){
num+=1;
check=false;
find(i,1,num);
}
printf("%d\n",ans);
return 0;
}
T3:
刚看到题的时候感觉像是一个最短路??状压??
但是感觉应该写不完,所以直接写的暴力。
后来听了TA爷说的,可以给操作编上号以后按照顺序直接dfs就行了。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int T,n,a[30],b[30],ans;
//1:三顺子 2:双顺子 3:单顺子 4:三带二,一 6:四带二
inline bool check(){
for(int i=1;i<=15;++i)
if(a[i]) return false;
return true;
}
inline void dfs(int x,int y){
int i,j,k,kind,sum=0;
if(y>=ans) return ;
if(check()){
ans=min(ans,y);
return ;
}
for(i=1;i<=13;++i) sum+=a[i]?1:0;
sum+=a[14]+a[15]?1:0; ans=min(ans,y+sum);
for(kind=x;kind<=5;++kind){
if(kind==1){
for(i=1;i<=11;++i)
if(a[i]>=3)
for(j=i+1;j<=12&&a[j]>=3;++j){
for(k=i;k<=j;++k) a[k]-=3;
dfs(x,y+1);
for(k=i;k<=j;++k) a[k]+=3;
}
}
if(kind==2){
for(i=1;i<=10;++i)
if(a[i]>=2&&a[i+1]>=2)
for(j=i+2;j<=12&&a[j]>=2;++j){
for(k=i;k<=j;++k) a[k]-=2;
dfs(x,y+1);
for(k=i;k<=j;++k) a[k]+=2;
}
}
if(kind==3){
for(i=1;i<=8;++i){
sum=0;
for(j=i;j<=i+3;++j)
if(a[j]) sum+=1;
if(sum!=4) continue;
for(j=i+4;j<=12&&a[j];++j){
for(k=i;k<=j;++k) a[k]-=1;
dfs(x,y+1);
for(k=i;k<=j;++k) a[k]+=1;
}
}
}
if(kind==4){
for(i=1;i<=15;++i)
if(a[i]>=3){
a[i]-=3;
for(j=1;j<=15;++j)
if(a[j]>=2)
a[j]-=2,dfs(x,y+1),a[j]+=2;
a[i]+=3;
}
for(i=1;i<=15;++i)
if(a[i]>=3){
a[i]-=3;
for(j=1;j<=15;++j)
if(a[j])
a[j]-=1,dfs(x,y+1),a[j]+=1;
a[i]+=3;
}
}
if(kind==5){
for(i=1;i<=15;++i)
if(a[i]>=4){
a[i]-=4;
for(j=1;j<=15;++j)
if(a[j]>=2){
a[j]-=2;
for(k=j;k<=15;++k)
if(a[k]>=2)
a[k]-=2,dfs(x,y+1),a[k]+=2;
a[j]+=2;
}
a[i]+=4;
}
for(i=1;i<=15;++i)
if(a[i]>=4){
a[i]-=4;
for(j=1;j<=15;++j)
if(a[j]){
a[j]-=1;
for(k=j;k<=15;++k)
if(a[k])
a[k]-=1,dfs(x,y+1),a[k]+=1;
a[j]+=1;
}
a[i]+=4;
}
}
}
}
int main(){
scanf("%d%d",&T,&n);
while(T--){
int i,j,x,y;
memset(a,0,sizeof(a));
for(i=1;i<=n;++i){
scanf("%d%d",&x,&y);
if(!x) a[y+13]+=1;
if(x>=3) a[x-2]+=1;
if(x>0&&x<3) a[x+11]+=1;
}
ans=0;
for(i=1;i<=13;++i) ans+=a[i]?1:0;
ans+=a[14]+a[15]?1:0;
dfs(1,0);
printf("%d\n",ans);
}
}
Day2
T1:
二分答案+贪心验证。贪心的时候直接扫一遍就行了。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define mid (l+r)/2
const int N=50010;
int L,n,m,a[N];
inline bool check(int x){
int now=0,sum=0,i;
for(i=2;i<=n+2;++i){
if(a[i]-now>=x){
now=a[i];
}
else{
sum+=1;
}
}
return sum<=m;
}
int main(){
int i,j;
scanf("%d%d%d",&L,&n,&m);
a[n+2]=L;
for(i=1;i<=n;++i) scanf("%d",&a[i+1]);
int l=0,r=L,ans=0;
while(l<=r){
if(check(mid)) ans=max(ans,mid),l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
return 0;
}
T2:
首先70分的dp很好写,就是f[i][j][k]表示到第一个的i,第二个的j,分成了k段的答案。那么首先处理出到i,j时连续的相同的长度,就比较好转移了。
那么满分的就是将转移的时候的枚举改成前缀和就好了。注意这个题还卡内存,所以需要将k的那一维滚了。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define D 1000000007
const int M=210;
const int N=1010;
char s1[N],s2[M];
int n,m,k,f[2][N][M],sum[N][M];
inline char in(){
char ch=getchar();
while(ch<'a'|ch>'z') ch=getchar();
return ch;
}
int main(){
int i,j,p,now=0;
scanf("%d%d%d",&n,&m,&k);
sum[0][0]=1;
for(i=1;i<=n;++i) s1[i]=in(),sum[i][0]=1;
for(i=1;i<=m;++i) s2[i]=in();
for(i=1;i<=n;++i)
for(j=1;j<=m;++j)
sum[i][j]=(s1[i]==s2[j])?sum[i-1][j-1]:0;
while(k--){
now^=1;
memset(f[now],0,sizeof(f[now]));
for(i=1;i<=n;++i)
for(j=1;j<=min(m,i);++j)
f[now][i][j]=(f[now][i-1][j]+(s1[i]==s2[j]?sum[i][j]:0))%D;
for(i=0;i<=n;++i) sum[i][0]=0;
for(i=0;i<=m;++i) sum[0][i]=0;
for(i=1;i<=n;++i)
for(j=1;j<=m;++j)
sum[i][j]=(s1[i]==s2[j])?((sum[i-1][j-1]+f[now][i-1][j-1])%D):0;
}
printf("%d\n",f[now][n][m]);
}
T3:
又是二分答案,主要的问题是在二分之后我们怎样去判断呢?
对于一个二分的答案mid,我们首先找出所有总长度大于mid的路线,然后我们求出这些路线的交,再找出交中最大的边减去,看看符不符合。
对于上面说的我们可以先处理处经过每条边的路线的数目,怎样统计呢?
我们对于一条边x,y,和他们的lca,在节点x和y处+1,在lca处-2,这样我们遍历一遍所有的节点,统计每一个父亲处的权值和后,就可以算出这个父亲的上一条边有多少路径经过了。
但是这种做法会背卡5分的常数,UOJ上好像有线性的做法,有时间的时候写一写吧。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define mid (l+r)/2
#define inf 0x7fffffff
const int N=300010;
struct Q{int x,y,lca,dis;}q[N];
struct S{int st,en,va;}aa[N*2];
int n,m,tot,point[N],next[N*2],fa[N][20],deep[N],dis[N],maxlen,maxn,v[N];
inline int in(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void add(int x,int y,int z){
tot+=1;next[tot]=point[x];point[x]=tot;
aa[tot].st=x;aa[tot].en=y;aa[tot].va=z;
tot+=1;next[tot]=point[y];point[y]=tot;
aa[tot].st=y;aa[tot].en=x;aa[tot].va=z;
}
inline void prepare(int x,int last){
int i,j;
for(i=1;i<=19&&deep[x]>=(1<<i);++i)
fa[x][i]=fa[fa[x][i-1]][i-1];
for(i=point[x];i;i=next[i])
if(aa[i].en!=last){
deep[aa[i].en]=deep[x]+1;
fa[aa[i].en][0]=x;
dis[aa[i].en]=dis[x]+aa[i].va;
prepare(aa[i].en,x);
}
}
inline int LCA(int x,int y){
if(deep[x]<deep[y]) swap(x,y);
int t=deep[x]-deep[y],i;
for(i=0;i<=19;++i)
if(t&(1<<i)) x=fa[x][i];
for(i=19;~i;--i)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return x==y?x:fa[x][0];
}
inline int work(int x,int last){
int i,sum=v[x];
for(i=point[x];i;i=next[i])
if(aa[i].en!=last) sum+=work(aa[i].en,x);
if(sum==tot) maxn=max(maxn,dis[x]-dis[fa[x][0]]);
return sum;
}
int main(){
int i,j,x,y,z;
n=in();m=in();
for(i=1;i<n;++i){
x=in();y=in();z=in();
add(x,y,z);
}
prepare(1,0);
for(i=1;i<=m;++i){
q[i].x=in();q[i].y=in();
q[i].lca=LCA(q[i].x,q[i].y);
q[i].dis=dis[q[i].x]+dis[q[i].y]-2*dis[q[i].lca];
maxlen=max(maxlen,q[i].dis);
}
int l=0,r=maxlen;
while(l<r){
tot=maxn=0;
memset(v,0,sizeof(v));
for(i=1;i<=m;++i)
if(q[i].dis>mid)
tot+=1,v[q[i].x]+=1,v[q[i].y]+=1,v[q[i].lca]-=2;
work(1,0);
if(maxlen-maxn<=mid) r=mid;
else l=mid+1;
}
printf("%d\n",l);
}