20181023(模拟+矩阵快速幂及推公式+最短路+不知道什么DP)

NOIP欢乐%你赛
1. 小澳的方阵
(matrix.cpp/c/pas)
【题目描述】
小澳最近迷上了考古,他发现秦始皇的兵马俑布局十分有特点,热爱钻研的小澳打算在电脑上还原这个伟大的布局。
他努力钻研,发现秦始皇布置兵马俑是有一定规律的。兵马俑阵总共有n行m列,秦始皇在布置的时候每次会指定一行或一列,然后指定一个兵种,使得这一行或者这一列上全部放上这一个兵种。如果这一行上以前放过其它的兵种,那么他会拔掉以前的兵种改成现在他命令的兵种。
小澳从秦朝的文献中找到了布置这个方阵的操作顺序,他希望你能告诉他布局完成后整个兵马俑阵是什么样子的。
【输入格式】
输入文件为matrix.in
输入文件共q+1行。
输入文件第1行包括三个正整数n,m,q,分别表示兵马俑阵的行数和列数以及秦始皇总的操作数。
接下来q行,每行三个正整数,x,y,z,x表示操作种类,若x=1表示给第y行(y<=n)全部放上z这个兵种,若x=2,则表示给第y列(y<=m)全部放上z这个兵种,数据保证z<=2^31-1。
【输出格式】
输出文件名为matrix.out。
输出文件共n行,每行m个整数,分别用空格隔开。表示最后方阵上每个位置放的兵种,如果某个位置没有被操作过输出0。
【输入输出样例1】
matrix.in
3 3 3
1 1 3
2 2 1
1 2 2
matrix.out
3 1 3
2 2 2
0 1 0
【输入输出样例1说明】
【输入输出样例2】
matrix.in
5 3 5
1 1 1
1 3 1
1 5 1
2 1 1
2 3 1
matrix.out
1 1 1
1 0 1
1 1 1
1 0 1
1 1 1
【数据规模与约定】
对于前20%的数据,nm<=25。
对于前30%的数据,q<=2000。
对于100%的数据,n,m<=1000,n
m<=105,q<=106。
【提示】
因为本题输入文件较大,使用c++的选手可能会因为使用cin和cout导致超时。
解决方法是可以加库后使用scanf()函数和printf()来避免这种问题的出现。如果有不会使用上面两个输入输出函数的选手可以向在场的工作人员或监考老师求助。
2. 小澳的坐标系
(coordinate.cpp/c/pas)
【题目描述】
小澳者表也,数学者景也,表动则景随矣。
小澳不喜欢数学,可数学却待小澳如初恋,小澳睡觉的时候也不放过。
小澳的梦境中出现了一个平面直角坐标系,自原点,向四方无限延伸。
小澳在坐标系的原点,他可以向上、向左或者向右走。他可以走n步,但不能经过相同的点。
小澳想知道他有多少种走法。
【输入格式】
输入文件名为coordinate.in
输入文件仅第一行一个正整数n,表示小澳可以走的步数。
【输出格式】
输出文件名为coordinate.out。
输出文件共一行,输出一个正整数,表示答案(对10^9+7取模)。
【输入输出样例1】
coordinate.in
2
coordinate.out
7
【输入输出样例1说明】
从(0,0)出发走2步,共7种走法:
(0,0)->(0,1)->(0,2)
(0,0)->(0,1)->(1,1)
(0,0)->(0,1)->(-1,1)
(0,0)->(1,0)->(2,0)
(0,0)->(1,0)->(1,1)
(0,0)->(-1,0)->(-2,0)
(0,0)->(-1,0)->(-1,1)
【输入输出样例2】
coordinate.in
3
coordinate.out
17
【数据规模与约定】
测试点编号
n
1~2
n<=10
3~4
n<=100
5~6
n<=1000
7~8
n<=10^6
9~10
n<=10^9
3.小澳的葫芦
(calabash.cpp/c/pas)
【题目描述】
小澳最喜欢的歌曲就是《葫芦娃》。
一日表演唱歌,他尽了洪荒之力,唱响心中圣歌。
随之,小澳进入了葫芦世界。
葫芦世界有n个葫芦,标号为1~ n。n个葫芦由m条藤连接,每条藤连接了两个葫芦,这些藤构成了一张有向无环图。小澳爬过每条藤都会消耗一定的能量。
小澳站在1号葫芦上(你可以认为葫芦非常大,可以承受小澳的体重),他想沿着藤爬到n号葫芦上,其中每个葫芦只经过一次。
小澳找到一条路径,使得消耗的能量与经过的葫芦数的比值最小。
【输入格式】
输入文件名为calabash.in
输入文件第一行两个正整数n,m,分别表示葫芦的个数和藤数。
接下来m行,每行三个正整数u,v,w,描述一条藤,表示这条藤由u连向v,小澳爬过这条藤需要消耗w点能量。
【输出格式】
输出文件名为calabash.out。
一行一个实数,表示答案(误差不超过 10^-3)。
【输入输出样例】
calabash.in
4 6
1 2 1
2 4 6
1 3 2
3 4 4
2 3 3
1 4 8
calabash.out
2.000
【输入输出样例说明】
有4种爬法:
1->4,消耗能量8,经过2个葫芦,比值为8/2=4。
1->2->4,消耗能量1+6=7,经过3个葫芦,比值为7/3≈2.33。
1->3->4,消耗能量2+4=6,经过3个葫芦,比值为6/3=2。
1->2->3->4,消耗能量1+3+4=8,经过4个葫芦,比值为8/4=2。
所以选第三种或第四种方案,答案为2。

艾比所特(mixedblood)
Time Limits:2000ms Memory Limits:256MB
Description
阿良良木历将要迎来人生(不,是吸血鬼生涯)的第二次战斗——与身为
人类和吸血鬼混血儿的艾比所特在直江津高中的操场solo,以取回
Heartunderblade 的左脚。
艾比所特个子不大,却单手扛着一副形状比自己大三倍,重量是他体重乘
三次方的巨大十字架。吸血鬼是怕十字架的,所以历要远远地躲着。更麻烦的
是艾比所特能变成雾气,历攻击不到他,他却因为是混血儿,吸血鬼的弱点降
到了mininum,可以碰十字架。直到历用沙子使艾比所特显形,历才获得胜利。
可以把直江津高中的操场看成排成一条直线的n 个点,其中第i 个点位于
数轴上的位置x[i]。如果从i 跳到j,首先要花费时间|x[j]-x[i]|。假如i<j,
还需额外花费时间d[i]+a[j],如果i>j,需额外花费时间c[i]+b[j]。
历在战后开始考虑一个新的问题。首先定义艾比所特的攻击:历跳到一个
点,然后艾比所特会向这个点扔一个十字架,有十字架的点历无法再次到达。
历每次行动可以选择若干个没有十字架的点,然后在其中一个点开始(此时艾
比所特不会向这个点扔十字架),按某种顺序跳过所有点,最终跳回开始的点。
经过若干次这样的行动后,他最多能拖延多少时间。(一次行动到下一次之间
不会消耗时间)
好吧历承认他算不出答案,你能帮帮他吗?
Input
第一行一个整数n。
接下来一行n 个整数,表示x 数组(x 是严格递增的)
接下来四行,每行n 个正整数,分别表示a,b,c,d 数组。
Output
1 行,表示答案。
Sample Input
3
1 2 5
2 2 2
3 4 3
3 3 2
5 1 1
Sample Output
27

T1:

题意概括:给矩阵染色,每次给一行或者一列染色,求最后染色情况

不要被数据吓到,该怎么打怎么打,虽然是第一题但是怎么也要动动脑筋。
一般这种类型是不是都是从下往上覆盖?
是不是询问很多?
那就记每行每列的颜色
顺便记一下该颜色的序号,因为一个点只可能由该点的行或者列的颜色决定,
所以就是行或者列最终颜色的序号大的那个颜色

#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
struct node{
	int num,id;
}heng[maxn],shu[maxn];
int n,m,a[maxn][maxn],q;
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	for (int i=1; i<=q; i++)
	  {
	  	int x,y,z;
	  	scanf("%d%d%d",&x,&y,&z);
	  	if (x==1) heng[y].num=z,heng[y].id=i;
	  	if (x==2) shu[y].num=z,shu[y].id=i;
     }
    for (int i=1; i<=n; i++)
      {
      	for (int j=1; j<=m; j++)
      	  if (heng[i].id>shu[j].id) printf("%d ",heng[i].num); else printf("%d ",shu[j].num);
      	printf("\n");
      }
}

T2

题意概括:该人在平面直角坐标系上只能往上往右或者往左走,求走 i i 步有多少中路径

我的思考顺序:
f [ i ] [ 0 / 1 / 2 ] f[i][0/1/2] 分别表示走第 i i 步要往哪里走,0:往上,1:往左,2:往右
防止重复的话,往左之后只能往上或者往左,往右亦然
往上走则自由
由此发现
可以省略第二维
f [ i ] = 2 f [ i 1 ] + f [ i 2 ] f[i]=2*f[i-1]+f[i-2]
再用矩阵快速幂出解

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
const int mod=1e9+7;
int n;
int a[4][4],f[4],bas[4][4],tmp[4][4];
void muti()
{
    memset(tmp,0,sizeof(tmp));
    for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++)
            for(int k=1;k<=3;k++)
                tmp[i][j]=(tmp[i][j]+a[i][k]*bas[k][j])%mod;
    memcpy(a,tmp,sizeof(a));
}
void Muti()
{
    memset(tmp,0,sizeof(tmp));
    for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++)
            for(int k=1;k<=3;k++)
                tmp[i][j]=(tmp[i][j]+bas[i][k]*bas[k][j])%mod;
    memcpy(bas,tmp,sizeof(bas));
}
void pow(int k)
{
    if(k==0) return;
    if(k%2==1) muti();
    Muti();
    pow(k/2);
}
signed main()
{
    scanf("%lld",&n);
    a[1][1]=a[1][2]=a[1][3]=a[2][1]=a[2][2]=a[3][1]=a[3][3]=1;
    memcpy(bas,a,sizeof(bas));
    pow(n-2);
    for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++)
            f[i]=(f[i]+a[j][i])%mod;
    printf("%lld",(f[1]+f[2]+f[3])%mod);
    return 0;
}

T3

题意:有向无环图上,求最少路过的边权和与点的比值。
这题一开始想打广搜骗暴力分,后来觉得好像emmm
边最多就2000
spfa啊
d i s [ x ] [ y ] dis[x][y] 表示走到x号点时已走了y个点(包括x号点)的最短距离,然后答案就是 m i n ( d i s t ( n , i ) i ) min(\frac{dist(n,i)}{i})

#include<bits/stdc++.h>
using namespace std;
const int maxn=210;
const int maxm=2010;
int v[maxm<<1],go[maxm<<1],nxt[maxm<<1],head[maxm<<1],dis[maxn][maxn],d[maxn*maxn][2];
int tot,m,n,t,w,now,num;
double ans;
bool vis[maxn][maxn];
void add(int x,int y,int z)
{
    go[++tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
    v[tot]=z;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1; i<=m; i++)
    {
    	int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
    }
    memset(dis,127,sizeof(dis));
    t=0;w=1;
    dis[1][1]=0;
    d[1][0]=d[1][1]=1;
    while (t<w)
    {
        t++;
        now=d[t][0];num=d[t][1];
        for(int i=head[now];i;i=nxt[i])
        {
            int to=go[i];
            if (dis[now][num]+v[i]<dis[to][num+1])
            {
                dis[to][num+1]=dis[now][num]+v[i];
                if (!vis[to][num+1])
                {
                    w++;
                    d[w][0]=to;
                    d[w][1]=num+1;
                    vis[to][num+1]=1;
                }
            }
        }
        vis[now][num]=0;
    }
    ans=2139062143;
    for (int i=2; i<=n; i++)
        if (dis[n][i]!=2139062143 && (double)dis[n][i]/i<ans)
            ans=(double)dis[n][i]/i;
    printf("%.3lf",ans);
}

T4

题意:走简单环,即每个点只有1的出度和入度,问题就是找权值最大的简单环。

对于一个简单环来说,一个点有且只有一个出边和一个入边。于是我们设出状态F[I][J][K]表示当前做到第i个点,前i个点中有j个点出边连向编号比i大的点,有k个点入边连向编号比i大的点。
分类讨论点 i i 的出边和入边情况:
1、点i连向的出边和入边都编号都小于 i i 。这时我们发现第i个点对答案的贡献实为 a [ i ] + c [ i ] + x [ i ] 2 a[i]+c[i]+x[i]*2 ,而由此可以由 f [ i 1 ] [ j ] [ k ] f[i-1][j][k] 转移到 f [ i ] [ j 1 ] [ k 1 ] f[i][j-1][k-1]
2、点i连向的出边和入边都编号都大于 i i 。这时我们发现第i个点对答案的贡献实为 b [ i ] + d [ i ] x [ i ] 2 b[i]+d[i]-x[i]*2 ,而由此可以由 f [ i 1 ] [ j ] [ k ] f[i-1][j][k] 转移到 f [ i ] [ j + 1 ] [ k + 1 ] f[i][j+1][k+1]
3、点i连向的出边编号大于i和入边编号小于 i i 。这时我们发现第i个点对答案的贡献实为 d [ i ] + a [ i ] d[i]+a[i] ,而由此可以由 f [ i 1 ] [ j ] [ k ] f[i-1][j][k] 转移到 f [ i ] [ j ] [ k ] f[i][j][k]
4、点i连向的出边编号小于 i i 和入边编号大于 i i 。这时我们发现第i个点对答案的贡献实为 c [ i ] + b [ i ] c[i]+b[i] ,而由此可以由 f [ i 1 ] [ j ] [ k ] f[i-1][j][k] 转移到 f [ i ] [ j ] [ k ] f[i][j][k]

对于 f [ i 1 ] [ j ] [ k ] f[i-1][j][k] ,当你i选择一个编号比自己小的入边或出边时,实际上也从 j j 个连向编号比 i 1 i-1 大的点的出边或k个连向编号比 i 1 i-1 大的点的入边中减少了一条连向编号比i大的点的入边或出边。
可以发现,每次状态转移时j和k都是同时加减的,所以我们可以减少一维,只要设出 f [ i ] [ j ] f[i][j] 即可。时间复杂度为 O ( N 2 ) O(N^2)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5005;
int n,t,k,l;
ll a[maxn],b[maxn],c[maxn],d[maxn],f[maxn][maxn],x[maxn],p;
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%lld",&x[i]);
    for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
    for (int i=1;i<=n;i++) scanf("%lld",&b[i]);
    for (int i=1;i<=n;i++) scanf("%lld",&c[i]);
    for (int i=1;i<=n;i++) scanf("%lld",&d[i]);
    memset(f,-1,sizeof(f));
	f[0][0]=0;
    for (int i=0;i<n;i++)
        for (int j=0;j<=i;j++)
		  {
            if (f[i][j]==-1) continue;
            if (i!=0&&j!=0)
				{
				   f[i+1][j-1]=max(f[i+1][j-1],f[i][j]+x[i+1]*2+a[i+1]+c[i+1]);
                   if (i!=n-1)
				     {
                       f[i+1][j]=max(f[i+1][j],f[i][j]+a[i+1]+d[i+1]);
                       f[i+1][j]=max(f[i+1][j],f[i][j]+b[i+1]+c[i+1]);
                     }
                }
            if (i!=n-1) f[i+1][j+1]=max(f[i+1][j+1],f[i][j]+b[i+1]+d[i+1]-x[i+1]*2);
        }
    printf("%lld",f[n][0]);
}

预估分数:100+80(矩阵快速幂忘了。。)+100+100
实际:100+80+100+0(。。。对拍了一组数据错了,然后按照数据改程序改爆了)

扫描二维码关注公众号,回复: 3876763 查看本文章

猜你喜欢

转载自blog.csdn.net/beautiful_CXW/article/details/83306615