2014-10-5 NOIP模拟赛
祖孙询问
(tree.pas/c/cpp)
【问题描述】
已知一棵n个节点的有根树。有m个询问。每个询问给出了一对节点的编号x和y,询问x与y的祖孙关系。
【输入格式】
输入第一行包括一个整数n表示节点个数。
接下来n行每行一对整数对a和b表示a和b之间有连边。如果b是-1,那么a就是树的根。
第n+2行是一个整数m表示询问个数。
接下来m行,每行两个正整数x和y。
【输出格式】
对于每一个询问,输出1:如果x是y的祖先,输出2:如果y是x的祖先,否则输出0。
【样例输入】
10
234 -1
12 234
13 234
14 234
15 234
16 234
17 234
18 234
19 234
233 19
5
234 233
233 12
233 13
233 15
233 19
【样例输出】
1
0
0
0
2
【数据规模】
对于30%的数据,n,m≤1000。
对于100%的.据,n,m≤40000,每个节点的编号都不超过40000。
/* 树剖求lca,看x和y的lca是x还是y还是其他点 */ #include<iostream> #include<cstdio> using namespace std; #define maxn 40010 int n,m,fa[maxn],num,head[maxn],root,sz[maxn],son[maxn],top[maxn],dep[maxn]; struct node{ int to,pre; }e[maxn*2]; void Insert(int from,int to){ e[++num].to=to; e[num].pre=head[from]; head[from]=num; } void dfs1(int father,int now){ fa[now]=father; dep[now]=dep[father]+1; sz[now]=1; for(int i=head[now];i;i=e[i].pre){ int to=e[i].to; if(to==father)continue; dfs1(now,to); sz[now]+=sz[to]; if(sz[to]>sz[son[now]]||!son[now])son[now]=to; } } void dfs2(int father,int now){ top[now]=father; if(son[now])dfs2(father,son[now]); for(int i=head[now];i;i=e[i].pre){ int to=e[i].to; if(to==fa[now]||to==son[now])continue; dfs2(to,to); } } int lca(int a,int b){ while(top[a]!=top[b]){ if(dep[top[a]]<dep[top[b]])swap(a,b); a=fa[top[a]]; } if(dep[a]>dep[b])swap(a,b); return a; } int main(){ //freopen("Cola.txt","r",stdin); freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); scanf("%d",&n); int x,y; for(int i=1;i<=n;i++){ scanf("%d%d",&x,&y); if(y==-1)root=x; else{ Insert(x,y); Insert(y,x); } } dfs1(0,root); dfs2(root,root); scanf("%d",&m); for(int i=1;i<=m;i++){ scanf("%d%d",&x,&y); int k=lca(x,y); if(k==x){printf("1\n");continue;} if(k==y){printf("2\n");continue;} printf("0\n"); } return 0; }
比赛
(mat.pas/c/cpp)
【问题描述】
有两个队伍A和B,每个队伍都有n个人。这两支队伍之间进行n场1对1比赛,每一场都是由A中的一个选手与B中的一个选手对抗。同一个人不会参加多场比赛,每个人的对手都是随机而等概率的。例如A队有A1和A2两个人,B队有B1和B2两个人,那么(A1 vs B1,A2 vs B2)和(A1 vs B2,A2 vs B1)的概率都是均等的50%。
每个选手都有一个非负的实力值。如果实力值为X和Y的选手对抗,那么实力值较强的选手所在的队伍将会获得(X-Y)^2的得分。
求A的得分减B的得分的期望值。
【输入格式】
第一行一个数n表示两队的人数为n。
第二行n个数,第i个数A[i]表示队伍A的第i个人的实力值。
第三行n个数,第i个数B[i]表示队伍B的第i个人的实力值。
【输出格式】
输出仅包含一个实数表示A期望赢B多少分。答案保留到小数点后一位(注意精度)。
【样例输入】
2
3 7
1 5
【样例输出】
20.0
【数据规模】
对于30%的数据,n≤50。
对于100%的.据,n≤50000;A[i],B[i]≤50000。
/* 排序后只需枚举一个人i,用一个指针指着另一 队中实力比i弱的里面最强的人,维护实力值的前缀和,实力值平方的前缀和即可算出期望。 显然指针只可能向右移动,所以这一步是线性的。 我直接用了个二分 */ #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<vector> #define ll long long using namespace std; inline ll read(){ ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n; ll a[50005],b[50005]; ll s1[50005],s2[50005]; ll ans1,ans2; int find(int x){ int l=1,r=n,ans=0; while(l<=r){ int mid=(l+r)>>1; if(b[mid]<=x)l=mid+1,ans=mid; else r=mid-1; } return ans; } int main(){ freopen("mat.in","r",stdin); freopen("mat.out","w",stdout); n=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<=n;i++) b[i]=read(); sort(b+1,b+n+1); for(int i=1;i<=n;i++){ s1[i]=s1[i-1]+b[i]; s2[i]=s2[i-1]+b[i]*b[i]; } for(int i=1;i<=n;i++){ int t=find(a[i]); ans1+=t*a[i]*a[i]+s2[t]-2*s1[t]*a[i]; ans2+=(n-t)*a[i]*a[i]+(s2[n]-s2[t])-2*(s1[n]-s1[t])*a[i]; } printf("%.1lf",(double)(ans1-ans2)/n); return 0; }
数字
(num.c/cpp/pas)
【问题描述】
一个数字被称为好数字当他满足下列条件:
1. 它有2*n个数位,n是正整数(允许有前导0)。
2. 构成它的每个数字都在给定的数字集合S中。
3. 它前n位之和与后n位之和相等或者它奇数位之和与偶数位之和相等
例如对于n=2,S={1,2},合法的好数字有1111,1122,1212,1221,2112,2121,2211,2222这样8种。
已知n,求合法的好数字的个数mod 999983。
【输入格式】
第一行一个数n。
接下来一个长度不超过10的字符串,表示给定的数字集合。
【输出格式】
一行一个数字表示合法的好数字的个数mod 999983。
【样例输入】
2
0987654321
【样例输出】
1240
【数据规模】
对于20%的数据,n≤7。
对于100%的.据,n≤1000,|S|≤10。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define maxn 2010 #define mod 999983 int n,a[maxn],num[maxn],len; char ch[21]; int dfs(int pos,int v){ if(pos==n*2+1){ int w=0,x=0,y=0,z=0; for(int i=1;i<=2*n;i++){ if(i&1)w+=num[i];//奇数位 else x+=num[i]; if(i<=n)y+=num[i]; else z+=num[i]; } if(w==x||y==z)return 1; else return 0; } int ans=0; for(int i=1;i<=len;i++){ num[pos]=a[i]; ans=(ans+dfs(pos+1,a[i]))%mod; } return ans; } int main(){ //freopen("Cola.txt","r",stdin); freopen("num.in","r",stdin); freopen("num.out","w",stdout); scanf("%d%s",&n,ch+1); len=strlen(ch+1); for(int i=1;i<=len;i++)a[i]=ch[i]-'0'; cout<<dfs(1,0); }
/* ANS=前n位之和与后n位之和相等的方案数+奇数位之和与偶数位之和相等的方案数-前n位之和与后n位之和相等且奇数位之和与偶数位之和相等的方案数 前2个需要+的方案数直接递推,重点是最后一个要满足2个条件的方案数怎么求: 因为前n位之和=后n位之和,奇数位之和=偶数位之和 所以前n位中奇数位之和=后n位中偶数位之和 且 前n位中偶数位之和=后n位中奇数位之和 现在只要求上面这个问题的方案数 */ #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<vector> #define mod 999983 #define ll long long using namespace std; inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n; char ch[15]; int a[15]; int f[1005][9005]; ll ans; ll cal(int x) { ll ans=0; for(int i=0;i<=x*9;i++) if(f[x][i]) ans=(ans+((ll)f[x][i]*f[x][i]))%mod; return ans; } int main() { freopen("num.in","r",stdin); freopen("num.out","w",stdout); n=read(); scanf("%s",ch); int l=strlen(ch); for(int i=0;i<l;i++) a[i+1]=ch[i]-'0'; f[0][0]=1; for(int i=0;i<n;i++) for(int j=0;j<=n*9;j++) if(f[i][j]) for(int k=1;k<=l;k++) f[i+1][j+a[k]]=(f[i+1][j+a[k]]+f[i][j])%mod; ans=2*cal(n)-cal(n/2)*cal(n-n/2); printf("%d",(ans%mod+mod)%mod); return 0; }