【NOIP专题】NOIP2015

T1

神奇的幻方 水题,模拟就行。但是初赛可能会考幻方补全程序.

# include <iostream>
# include <cstdio>
# include <cstring>
using namespace std;
const int MAXN = 40*40;
int row[MAXN],col[MAXN];
int res[40][40];
int n;
int main(){
	scanf("%d",&n);
	res[1][(n+1)/2] = 1;
	row[1] = 1;col[1] = (n+1)/2;
	for(int i = 2;i<=n*n;i++){
		if(row[i-1]==1&&col[i-1]!=n){
		//若 (K-1) 在第一行但不在最后一列,则将 K 填在最后一行, (K-1) 所在列的右一列
			row[i] = n,col[i] = col[i-1]+1;
			res[row[i]][col[i]] = i;
		}else if(col[i-1]==n&&row[i-1]!=1){
			//若(K-1)在最后一列但不在第一行,则将K填在第一列,(K-1)所在行的上一行;
			col[i] = 1;row[i] = row[i-1]-1;
			res[row[i]][col[i]] = i;
		}else if(row[i-1]==1&&col[i-1]==n){
			//若(K-1)在第一行最后一列,则将K填在(K-1)的正下方;
			row[i] = row[i-1]+1;col[i] = col[i-1];
			res[row[i]][col[i]] = i;
		}else{
			if(res[row[i-1]-1][col[i-1]+1]==0){
				row[i] = row[i-1]-1;col[i] = col[i-1]+1;
				res[row[i]][col[i]] = i;
			}else{
				row[i] = row[i-1]+1;col[i] = col[i-1];
				res[row[i]][col[i]] = i;
			}
		}
	}
	for(int i = 1;i<=n;i++){
		for(int j = 1;j<=n;j++){
			printf("%d ",res[i][j]);
		}
		puts("");
	}
	return 0;
}

T2

信息传递 就是求最小环

# include<cstdio>
# include<cstring>
# include<algorithm>
# define N 200005
# define M 200005
# define inf (1ll<<31ll)-1
using namespace std;
int t,cnt,sum,top,ans=inf;
int low[N],dfn[N],sta[N];
int first[N],v[M],next[M];
bool insta[N];
void add(int x,int y)
{
	t++;
	next[t]=first[x];
	first[x]=t;
	v[t]=y;
}
void Tarjan(int x)
{
	int i,k;
	cnt++;
	low[x]=cnt;
	dfn[x]=cnt;
	top++;
	sta[top]=x;
	insta[x]=true;
	k=first[x];
	for(i=first[x];i;i=next[i])
	{
		k=v[i];
		if(!dfn[k])
		{
			Tarjan(k);
			low[x]=min(low[x],low[k]);
		}
		else if(insta[k])
		  low[x]=min(low[x],dfn[k]);
	}
	if(low[x]==dfn[x])
	{
		int sum=0;
		do
		{
			sum++;
			i=sta[top--];
			insta[i]=false;
		}
		while(i!=x);
		if(sum!=1)
		  ans=min(ans,sum);
	}
}
int main()
{
	int n,i,x;
	scanf("%d",&n);
	for(i=1;i<=n;++i)
	{
		scanf("%d",&x);
		add(i,x);
	}
	for(i=1;i<=n;++i)
	  if(!dfn[i])
	    Tarjan(i);
	printf("%d",ans);
	return 0;
}

T3

斗地主 模拟题,策略就是先找顺子,再找带的,最后出单牌。反正这题是因为数据水,这种策略其实是错误的。

# include <iostream>
# include <cstring>
# include <cstdio>
using namespace std;
const int MAXN = 16;
int card[MAXN];
void add(int v){
	if(v>=3&&v<=10)card[v-2]++;
	if(v==1)card[12]++;
	if(v==11)card[9]++;
	if(v==12)card[10]++;
	if(v==13)card[11]++;
	if(v==0)card[14]++;
	if(v==2)card[13]++;
}
int ans;
void dfs(int x){
	if(x>=ans)return;
	int k = 0;
	for(int i = 1;i<=12;i++){//顺子
		if(!card[i])k = 0;
		else{
			k++;
			if(k>=5){
				for(int j = i;j>=i-k+1;j--)card[j]--;
				dfs(x+1);
				for(int j = i;j>=i-k+1;j--)card[j]++;
			}
		}
	}
	k = 0;
	for(int i = 1;i<=12;i++){//双顺子 
		if(card[i]<=1)k = 0;
		else{
			k++;
			if(k>=3){
				for(int j = i;j>=i-k+1;j--)card[j]-=2;
				dfs(x+1);
				for(int j = i;j>=i-k+1;j--)card[j]+=2;
				}
		}
	}
	k = 0;
	for(int i = 1;i<=12;i++){//三顺子 
		if(card[i]<=2)k = 0;
		else{
			k++;
			if(k>=2){
				for(int j = i;j>=i-k+1;j--)card[j]-=3;
				dfs(x+1);
				for(int j = i;j>=i-k+1;j--)card[j]+=3;
				k = 0;
			}
		}
	}
	for(int i = 1;i<=13;i++){
		if(card[i]==3){
			card[i]-=3;
			for(int j = 1;j<=14;j++){//三带一 
				if(card[j]&&j!=i){
					card[j]--;
					dfs(x+1);
					card[j]++;
				}
			}
			for(int j = 1;j<=13;j++){//三代一对 
				if(card[j]>=2&&i!=j){
					card[j]-=2;
					dfs(x+1);
					card[j]+=2;
				}
			}
			card[i]+=3;
		}else if(card[i]==4){
			card[i]-=3;
			for(int j = 1;j<=14;j++){//三带一 
				if(card[j]&&j!=i){
					card[j]--;
					dfs(x+1);
					card[j]++;
				}
			}
			for(int j = 1;j<=13;j++){//三代一对 
				if(card[j]>=2&&i!=j){
					card[j]-=2;
					dfs(x+1);
					card[j]+=2;
				}
			}
			card[i]+=3;
			card[i]-=4;
			for(int j = 1;j<=14;j++){//四带一 
				if(card[j]>0&&j!=i){
					for(int k = 1;k<=14;k++){
						if(j==k&&j==14&&card[14]==2){//四带大小王 
							card[14]-=2;
							dfs(x+1);
							card[14]+=2;
						}else if(j!=k&&i!=k&&card[k]>0){
							card[j]--;card[k]--;
							dfs(x+1);
							card[j]++;card[k]++;
						}
					}
				}
			}
			for(int j = 1;j<=13;j++){
				if(j!=i&&card[j]>=2)
				for(int k = 1;k<=13;k++){
					if(j!=k&&i!=k&&card[k]>=2){
						card[j]-=2;card[k]-=2;
						dfs(x+1);
						card[j]+=2;card[k]+=2;
					}
				}
			} 
			card[i]+=4;
		}
	} 
	for(int i = 1;i<=14;i++)if(card[i])x++;
	ans = min(ans,x);
}

int main(){
	int T,n;
	scanf("%d %d",&T,&n);
	while(T--){
		memset(card,0,sizeof(card)); 
		int x,y;
		ans = n;
		for(int i = 1;i<=n;i++){
			scanf("%d %d",&x,&y);
			add(x);
		}
		dfs(0);
		printf("%d\n",ans);
	}
	return 0;
}

T4

跳石头 二分板子题

# include<cstdio> 
int read(){
	int flag = 1,n = 0;
	char ch;
	ch = getchar();
	while(ch>'9'||ch<'0'){
		if(ch=='-')flag = -1;
		ch = getchar();
	}
	while(ch>='0'&&ch<='9'){
		n*=10;
		n+=ch-'0';
		ch = getchar();
	}
	return n*flag;
}

int L,M,N;
int stones[50010];
bool check(int l){
	int tot = 0;
	int last = 0;
	for(int i = 1;i<=N;i++){
		if(stones[i]-last<l){
			tot++;
		}else{
			last = stones[i];
		}
		if(tot>M)return false;
	}
	if(L-last<l)tot++;
	return tot<=M;
}
int main(void){
	L = read();N = read();M = read();
	for(int i = 1;i<=N;i++){
		stones[i] = read();
	}
	int l = 0;int r = L;
	while(l<r){
		int mid = (l+r+1)>>1;
		if(check(mid)){
			l = mid;
		}else{
			r = mid-1;
		}
	}
	printf("%d",l);
	return 0;
}

T5

子串 显然是个DP。dp[i][j][k][0/1]表示A匹配i位B匹配j位分成k段,第i位选不选。 则有如下转移 若A[i]=B[j] $dp[i][j][k][0] = dp[i-1][j][k][0]+dp[i-1][j][k][1]$ $dp[i][j][k][1] = dp[i-1][j-1][k-1][0]+dp[i-1][j-1][k-1][1]+dp[i-1][j-1][k][1]$ 否则 $dp[i][j][k][0] = dp[i-1][j][k][0]+dp[i-1][j][k][1]$ $dp[i][j][k][1] = 0$ 代码:

# include <iostream>
# include <cstring>
# include <cstdio>
using namespace std;
const int MAXN = 1010;
const int MAXM = 210;
const int mod = 1000000007;
int dp[2][MAXM][MAXM][2];
char A[MAXN],B[MAXN];
int n,m,kk;
void write(int x){
	if(x>9)write(x/10);
	putchar(x%10+'0');
}

int main(){
	scanf("%d%d%d",&n,&m,&kk);
	scanf("%s %s",A+1,B+1);
	dp[1][0][0][0] = dp[0][0][0][0] = 1;
	for(register int i = 1;i<=n;i++){
		for(register int j = 1;j<=m;j++){
			for(register int k = 1;k<=kk;k++){
				if(A[i]==B[j]){
					dp[i&1][j][k][0] = (dp[(i-1)&1][j][k][0]+dp[(i-1)&1][j][k][1])%mod;
					dp[i&1][j][k][1] = (dp[(i-1)&1][j-1][k][1]+(dp[(i-1)&1][j-1][k-1][1]+dp[(i-1)&1][j-1][k-1][0])%mod)%mod;
				}else{
					dp[i&1][j][k][0] = (dp[(i-1)&1][j][k][0]+dp[(i-1)&1][j][k][1])%mod;
					dp[i&1][j][k][1] = 0;
				}
			}
		}
	}
	write((dp[n&1][m][kk][0]+dp[n&1][m][kk][1])%mod);
	return 0;
} 

T6

运输计划 可以一眼看出是个二分答案。然后统计出长度大于mid的路径的条数。考虑如何统计一条边被多少条路径经过,考虑树上差分。sum[x]++,sum[y]++,sum[lca]-=2;即可从底到顶统计出来。然后找到一条长度大于mid的,被超过num条路径经过的边即可。 代码:

# include<cstdio>
# include<cstring>
# define M 300001
# include<algorithm>
using namespace std;
struct nn{
    int to,next,val;
};
nn ed[M<<1];
struct nnn {
    int a,b,ans,dist;
};
nnn lca[M];
int head[M],tot,b[M];
int n,m,ans,l,r;
int f[M][31],dep[M],dis[M],sum[M];
inline int read() {
    int x=0;char c=getchar();
    while(c>'9'||c<'0') c=getchar();
    while(c>='0'&&c<='9') x=10*x+c-48,c=getchar();
    return x;
}
inline void add(int x,int y,int z) {
    ed[++tot].to=y;
    ed[tot].val=z;
    ed[tot].next=head[x];
    head[x]=tot;
}
inline void dfs(int x,int from,int de,int l) {
    dep[x]=de;
    dis[x]=l;
    f[x][0]=from;
    for(int i=head[x];i;i=ed[i].next) {
        int v=ed[i].to;
        if(v!=from) {
            b[v]=i;
            dfs(v,x,de+1,l+ed[i].val);
        }
    }
}
inline int LCA(int a,int b) {
    if(dep[a]<dep[b]) swap(a,b);
    int t=dep[a]-dep[b];
    for(int i=0;i<=30;i++)
      if((1<<i)&t) a=f[a][i];
    if(a==b) return a;
    for(int i=30;i>=0;i--) 
      if(f[a][i]!=f[b][i])
        a=f[a][i],b=f[b][i];
    return f[a][0];
}
inline void updata(int now,int from) {
    for(int i=head[now];i;i=ed[i].next) {
        int v=ed[i].to;
        if(v!=from) {
            updata(v,now);
            sum[now]+=sum[v];
        }
    }
}
inline bool check(int x) {
    int cnt=0,dec=0;
    memset(sum,0,sizeof(sum));
    for(int i=1;i<=m;i++)
      if(lca[i].dist>x) {
        cnt++;
        sum[lca[i].a]++;
        sum[lca[i].b]++;
        sum[lca[i].ans]-=2;
        dec=max(dec,lca[i].dist-x);
      }
    updata(1,1);
    for(int i=1;i<=n;i++) if(sum[i]==cnt&&ed[b[i]].val>=dec) return true;
    return false;
}
int main() {
    n=read();m=read();
    for(int i=1;i<n;i++) {
        int x,y,z;
        x=read();y=read();z=read();
        add(x,y,z);add(y,x,z);
    }

    dfs(1,1,0,0);
    for(int j=1;j<=30;j++)
      for(int i=1;i<=n;i++)
        f[i][j]=f[f[i][j-1]][j-1];
    for(int i=1;i<=m;i++) {
        lca[i].a=read();
        lca[i].b=read();
        lca[i].ans=LCA(lca[i].a,lca[i].b);
        lca[i].dist=dis[lca[i].a]+dis[lca[i].b]-(dis[lca[i].ans]<<1);
        r=max(r,lca[i].dist);
    }
    r++;
    while(l<r) {
        int mid=(l+r)>>1;
        if(check(mid)) ans=r=mid;
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}