2020智算之道复赛

数字

共 5 个测试点
每个测试点 20 分

每个测试点限时 1 秒
运行内存上限 512MB

咕咕有两个数字 a,b ,它忘记了 a 的前三位(记除去前三位的 a 为 aa ),但它记得 a 与 b 满足 a≡0(mod b),即 a 是 b 的倍数。
请你帮它求出有多少种满足条件的 a。
注意: a 没有前导 0

输入格式

一行两个整数,表示 a 和 b 。

输出格式

一行一个整数,表示有多少种满足条件的 a 。
数据范围与约定

对于前 20% 的数据,保证 b=1。

对于前 40% 的数据,保证 b≤2。

对于 100% 的数据,保证 0≤a≤10^14,0<b≤10^180.

样例输入

0 1

样例输出

900

样例解释

任何整数都是 1 的倍数,故 a 的前三位是 100999 中任何一种情况都可以满足条件。

思路

签到题,就这一个ac了,计算出a的长度,用pow函数制造出100-999的前缀,一个个的取模
#include
#include
#include
#include
using namespace std;
#define IO ios::sync_with_stdio(0); cin.tie(0);//快读
int main()
{
	IO;
	long long a,b;
	cin>>a>>b;
	long long len=0,k=a,ans=0;//十年oi一场空,不开longlong见祖宗
	while(k)
	{
		k/=10;
		len++;
	}
	for(int i=100;i<=999;i++)
	{
		long long j=i*pow(10,len);//计算出缺少的前缀,一个个的尝试
		if((j+a)%b==0)ans++;
	 } 
	 cout<<ans<<endl;
    return 0;
}

网格

共 5 个测试点
每个测试点 20 分

每个测试点限时 1 秒
运行内存上限 512MB

有一个 n∗n 的网格,左下角是 (0,0)右上角是 (n,n) 。

咕咕要从左下角移动到右上角,移动规则如下:
    若咕咕位于 (a,b) ,它可以花费 w1 个金币到达 (a,b+1)(a+1,b) 。
    若 (a,b) 是一个魔法格点,咕咕既可以选择 1 ,也可以选择花费 w2 个金币到达 (a+1,b+1) 。

咕咕想知道从左下角到达右上角最少需要花费多少个金币,请你帮帮它。

输入格式

第一行四个整数 n,k,w1,w2 。

接下来 k 行,第 i 行两个整数 xi,yi ,表示第 i 个魔法格点的坐标。

输出格式

一行一个整数,表示咕咕最少需要花费多少金币。
数据范围与约定

对于 20% 的数据,保证 n≤3 。

对于另 20% 的数据,保证魔法格点只出现在 (0,0)(n,n) 的对角线上,也就是对于任意的 i∈[1,k] , xi=yi​ 。

对于 100% 的数据,保证 1≤n,w1,w2≤109,0≤k≤2000,0≤xi,yi<n1 ,魔法格点坐标两两不同。

样例输入

3 2 132 100
0 1
1 2

样例输出

464

样例解释

能让咕咕花费最少的路径如下:
(0,0)(0,1)(1,2)(2,3)(3,3)(0,1),(1,2) 选择 2 (花费 100 金币) ,在其他格点选择 1 (花费 132 金币)。

思路:

据说是最长上升子序列...菜鸡不会,找了个题解看了看

选择1走一步,花费为w1。选择2走两步,花费为w2。
先考虑只走选择1,则到(x,y)的花费为(x+y)∗w1

(x+y)∗w1。

再考虑可以有选择2,我们要用魔法个点来优化之前的选择,则可以将魔法节点排序,那么就可以保证后面的魔法节点无法走到前面,这就无后效性了。再找子问题:

定义f[i]
f[i]为到达第ii个魔法节点的距离,则有
num=a[i].x+a[i].y−a[j].x−a[j].y−2num=a[i].x+a[i].y−a[j].x−a[j].y−2
f[i]=min(f[i],f[j]+num∗w1+w2)f[i]=min(f[i],f[j]+num∗w1+w2)

代码:

#include
#include
#include 
#include 
using namespace std;
typedef long long ll;
const int maxn = 2005;
ll f[maxn];
struct Node //结构体 
{
    int x,y;
}a[maxn];

int cmp(Node a,Node b) {//结构体排序 
    if(a.x != b.x) return a.x < b.x;
    return a.y < b.y;
}
int main()
{
    int n,k,w1,w2;
	scanf("%d%d%d%d",&n,&k,&w1,&w2);
    for(int i = 1;i <= k;i++) 
	{
        scanf("%d%d",&a[i].x,&a[i].y);
    }
    sort(a + 1,a + 1 + k,cmp);//给魔法节点排个序 
    a[k + 1].x = n;
	a[k + 1].y = n;
    k++;
    for(int i = 1;i <= k;i++) 
	{
        f[i] = 1ll * w1 * (a[i].x + a[i].y);
    }
    
    for(int i = 2;i <= k;i++)
	 {
        for(int j = 1;j < i;j++)
		 {
            if(a[j].x < a[i].x && a[j].y < a[i].y) 
			{
                int num = a[i].x + a[i].y - a[j].x - a[j].y - 2;
                f[i] = min(f[i],f[j] + 1ll * num * w1 + w2);
            }
        }
    }
    
    printf("%lld\n",f[k]);
    return 0;
}

有向无环图

共 5 个测试点
每个测试点 20 分

每个测试点限时 1 秒
运行内存上限 512MB

给定 k ,你需要生成一个没有重边的有向无环图(点编号 1-n ),使得从 1 到 n 的路径数恰好为 k 。

输入格式

一行两个正整数 k,N,表示要求的路径数和该测试点允许的最大点数(即你生成的图点数不得超过 N )。
输出格式

第一行两个正整数 n,m,表示你生成的图的点数和边数。

接下来 m 行,每行两个正整数 u,v ,表示有一条从 u 到 v 的有向边。

如有多个满足条件的图,输出任意一个。

数据范围与约定

测试点编号	N	k
1	=2	=1
2	=4	=3
3 =10^3500
45 =66  <2^64

对于 100% 的数据,满足 0<k<2^64 。

除了前面的要求,你的输出还要满足 1≤m≤10^5和 n>1 。

注意:输出中多余的空格和回车、边的输出顺序不影响得分,但多余的其他字符及不合法的输入会导致不得分。
样例输入
2 1000

样例输出

3 3
1 2
2 3
1 3

样例解释

两条路径分别为:

123
13

代码:

#pragma GCC optimize(2)

#include
#include
#include 
#include 
#include
#include 

using namespace std;

typedef unsigned long long ll;

vector<pair<int,int> >vec;

int main() {
    ll n,k;scanf("%llu%llu",&k,&n);
    
    ll tmp = k;
    int cnt = 0;
    while(tmp) {
        cnt++;
        tmp /= 2;
    }
    
    if(n == 2 && k == 1) {
        printf("2 1\n");
        printf("1 2\n");
        return 0;
    } else if(n == 4 && k == 3) {
        printf("4 5\n");
        printf("1 2\n2 3\n3 4\n1 3\n2 4\n");
        return 0;
    }
    
    int N = cnt + 2;
    for(int i = 2;i <= cnt + 1;i++) {
        for(int j = i + 1;j <= cnt + 2;j++) {
            vec.push_back({i,j});
        }
    }
    
    for(int i = 0;i < cnt;i++) {
        if((k >> i) & 1) {
            int nex = cnt - i + 1;
            vec.push_back({1,nex});
        }
    }
    
    printf("%d %d\n",N,(int)vec.size());
    for(int i = 0;i < vec.size();i++) {
        printf("%d %d\n",vec[i].first,vec[i].second);
    }
    return 0;
}


你可能感兴趣的:(算法)