T1 魔界大门
题意:
小蛮,龙幽,姜云凡拜入蜀山门下,多日之后闲来无事,忽然想去魔界查访神农鼎的下落,当他们到魔界大门时,却被守门卫士执宿,疚业拦住,无奈之中只能开打,胜利之后卫士消失,魔界大门却迟迟没有动静,这是眼尖的小蛮发现门上有许多的数字,门的上方写着0/1,懂得魔族语言的龙幽反复观察门上的文字,发现门上给出的是三个N*N 的矩阵A,B,C,当A*B=C 他们只要把门上的1全部按下去就能打开大门,否则需要按下所有的0。四个人的数学都不好,所以请你帮助他们判断A*B 是否与C 相等。
提示:矩阵乘法+随机化
考试的时候memset写错导致丢掉暴力40分QAQ
算法1:暴力矩阵乘法 期望得分40
但是O(n^3)算法铁定超时,所以我们想一想,为什么要全算完呢?
算法2:随机化坐标 我们可以rand()矩阵中的坐标,只计算这么一个坐标,之后与目标矩阵中的对应点比较,相同继续rand,不同直接跳出,但是也不能一直算,所以期望得分70~80
既然可以随机化坐标,那为什么不能随机化矩阵呢?
算法3:随机化矩阵 我们一次rand出一个n*1的矩阵,之后乘,每次O(n^2)效率,随机2~3次即可解决,期望得分100
#include
#include
#include
using namespace std;
const int MAXN=1020;
unsigned int T,n,a[MAXN][MAXN],b[MAXN][MAXN],c[MAXN][MAXN],d[MAXN][MAXN];
unsigned int A[MAXN][MAXN],B[MAXN][MAXN],C[MAXN][MAXN],t[MAXN];
bool init() {
memset(A,0,sizeof(A));
memset(B,0,sizeof(B));
memset(d,0,sizeof(d));
int i,j,k;
scanf("%d",&n);
for(i=1;i<=n;++i)
for(j=1;j<=n;++j)
scanf("%d",&a[i][j]);
for(i=1;i<=n;++i)
for(j=1;j<=n;++j)
scanf("%d",&b[i][j]);
for(i=1;i<=n;++i)
for(j=1;j<=n;++j)
scanf("%d",&c[i][j]);
for(i=1;i<=n;++i){
t[i]=rand()%100;
}
for(i=1;i<=1;++i){
for(j=1;j<=n;++j){
for(k=1;k<=n;++k){
A[i][j]+=t[k]*a[k][j];
}
}
}
for(i=1;i<=1;++i){
for(j=1;j<=n;++j){
for(k=1;k<=n;++k){
B[i][j]+=A[i][k]*b[k][j];
}
}
}
for(i=1;i<=1;++i){
for(j=1;j<=n;++j){
for(k=1;k<=n;++k){
d[i][j]+=t[k]*c[k][j];
}
if(d[i][j]!=B[i][j])
return 0;
}
}
return 1;
}
int main(){
scanf("%d",&T);
while(T--) {
if(init()) printf("TRUE\n");
else printf("FALSE\n");
}
}
T2 糖果
题意:
小姜找到了童话中“糖果国”,这里大到摩天大厦,小到小花小草都是用糖果建造而成的。更加神奇的是,天空中飘满了五颜六色的糖果云,很快糖果雨密密麻麻从天而落,红色的是草莓糖,黄色的是柠檬糖,绿色的是薄荷糖,黑色的是巧克力糖……
任何时候天空中所有的云朵颜色都不相同,不同颜色的云朵在不断地落下相应颜色的糖果。小姜发现天空中会不断出现一些云朵,而有的云朵在某一时刻又会自动消失,而云朵在存在时会不断地落下相应颜色的糖果,小姜有许多容量无限且袋口宽度不同的口袋,小姜完全接到一种糖果,当且仅当下落该种糖果的那朵云被袋口完全包含,小姜想知道每次他拿出一个袋口为[L,R]的口袋后他能完全接到多少种糖果。
提示:二维树状数组
考试的时候直接浮水法拿到暴力30分
算法1:浮水法 每当读入一个3区间,向上浮,能包住1区间就ans++,包住2区间ans—
算法2:观察数据特点,区间的范围较小,而区间[A,B]是区间[X,Y]的子集
的充要条件是X<=A<=Y 且X<=B<=Y,这样我们以区间左端点为X 轴坐标,以
区间右端点为Y 轴坐标,这样每个区间都唯一对应着平面上的一个点,以上三
种操作可以转化为删除平面上一个点,插入平面上一个点,查询一个矩形内点的
个数,可以用二维树状数组维护,时间复杂度O(m*logn*logn)
#include#include #include using namespace std; const int MAXN=1020; int n; int t[MAXN][MAXN]; void add(int x,int y,int d) { for(int i=x;i<=1015;i+=(i&(-i))) for(int j=y;j<=1015;j+=(j&(-j))) t[i][j]+=d; } int sum(int x,int y){ int res=0; for(int i=x;i;i-=(i&(-i))) for(int j=y;j;j-=(j&(-j))) res+=t[i][j]; return res; } int main(){ int i,l,r,flag; scanf("%d",&n); for(i=1;i<=n;++i) { scanf("%d%d%d",&flag,&l,&r); if(flag==1) add(l,r,1); if(flag==2) add(l,r,-1); if(flag==3) printf("%d\n",sum(r,r)-sum(l-1,r)); } return 0; }
T3 sta
题意:给出一个N个点的树,找出一个点来,以这个点为根的树时,所有点的深度之和最大
考试A掉
提示:DP
算法:DP f[x]=f[fa[x]]+n-2*size[x] fa[x]为x父节点 size[x]指以x为根的子树大小
首先我们找到任意一个节点进行深搜,统计出每棵子树的大小,以及所有点的深度之和
然后再以该节点为根深搜一遍,此时状态从父节点转移至子节点,转移方程如下:
f[x]=f[fa[x]]+n-2*size[x]
最后扫一遍数组即可出解
#include
#include
using namespace std;
const int MAXN=1000010;
typedef long long LL;
int n,tot,ans;
int h[MAXN];
int fa[MAXN];
LL size[MAXN],f[MAXN];
struct Edge{
int u,v,next;
}e[MAXN<<1];
void add(int u,int v) {
e[++tot].u=u;
e[tot].v=v;
e[tot].next=h[u];
h[u]=tot;
}
void dfs(int x) {
size[x]=1;
for(int i=h[x];i;i=e[i].next) {
int v=e[i].v;
if(v==fa[x]) continue;
fa[v]=x;
dfs(v);
size[x]+=size[v];
f[x]+=f[v]+size[v];
}
}
void dp(int x) {
if(x!=1)
f[x]=f[fa[x]]+n-2*size[x];
for(int i=h[x];i;i=e[i].next) {
if(e[i].v==fa[x]) continue;
dp(e[i].v);
}
}
int main(){
freopen("sta.in","r",stdin);
freopen("sta.ans","w",stdout);
int i,u,v;
scanf("%d",&n);
for(i=1;if[ans])
ans=i;
printf("%d",ans);
}
T4 会议
提示:最短路
算法分析:很明显需要以地区为点,以结界为顶点之间的边且权值为1,这
样能够得到一个新图,目标也就是求新图中的一个点使某些点到这个点的距离和
最小,由于魔教精英在魔法点上,所以计算时要枚举他第一步在哪块地区内,取
最小值即可。
主要说下构图问题,对于相邻的两个地区,相邻结界的两个魔法点端点给出
的顺序一定相反,即一个是(A,B),另一个是(B,A)。所以只需扫描所有地区
的周围魔法点,一旦发现有两个魔法点顺序相反且相邻,则对应两个地区之间有
边。
#include
#include
#include
using namespace std;
const int MAXN=600;
int n,tot;
int vis[MAXN];
int peo[MAXN],h[MAXN];
int dis[MAXN][MAXN];
int barrier[MAXN][MAXN][5];
int area[MAXN][MAXN];
struct Edge{
int u,v,w,next;
}e[MAXN<<2];
void add(int u,int v,int w) {
e[++tot].u=u;
e[tot].v=v;
e[tot].w=w;
e[tot].next=h[u];
h[u]=tot;
}
void spfa(int x) {
queueq;
memset(vis,0,sizeof(vis));
vis[x]=1;
dis[x][x]=0;
q.push(x);
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=h[u];i;i=e[i].next) {
int v=e[i].v;
if(v>n&&dis[x][v]>dis[x][u]+e[i].w) {
dis[x][v]=dis[x][u]+e[i].w;
if(!vis[v]) {
q.push(v);
vis[v]=1;
}
}
}
}
}
void init(){
int i,j,k;
memset(dis,0x3f,sizeof(dis));
scanf("%d%d%d",&area[0][0],&n,&peo[0]);
for(i=1;i<=peo[0];++i) scanf("%d",&peo[i]);
for(i=1;i<=area[0][0];++i) {
scanf("%d",&area[i][0]);
for(j=1;j<=area[i][0];++j) {
scanf("%d",&area[i][j]);
if(j!=1) {
int p=area[i][j],q=area[i][j-1];
barrier[p][q][++barrier[p][q][0]]=i;
barrier[q][p][++barrier[q][p][0]]=i;
}
add(area[i][j],i+n,0);add(i+n,area[i][j],0);
}
int p=area[i][1],q=area[i][area[i][0]];
barrier[p][q][++barrier[p][q][0]]=i;
barrier[q][p][++barrier[q][p][0]]=i;
}
for(i=1;i<=n;++i)
for(j=1;j<=n;++j)
if(barrier[i][j][0]!=0) {
add(barrier[i][j][1]+n,barrier[i][j][2]+n,1);
add(barrier[i][j][2]+n,barrier[i][j][1]+n,1);
}
for(i=1;i<=n;++i) spfa(i);
int ans=0x3fffffff;
int id=0;
for(i=n+1;i<=area[0][0]+n;++i) {
int temp=0;
for(j=1;j<=peo[0];++j){
temp+=dis[peo[j]][i];
}
if(ans>temp) {
ans=temp;
id=i-n;
}
}
printf("%d\n%d",ans,id);
while(1);
}
int main(){
init();
}