2019-2020 ICPC Asia Yinchuan Regional Contest【银川现场赛】

A. Girls Band Party

题目链接

Description

You are currently playing a game called “Garupa”. In an event of the game, you are trying to get more event points. You have nn cards, each with its own name, color, and power. When you play the game, you can put five cards of different names into your deck. The base point of this event is the sum of the power of the cards in your deck. On top of that, the event publishes a color and five names as bonus attributes, which means that each time you have a card with a bonus color in the deck, you end up with a 20% 20%20% increase in event point. And each time you have a card with a bonus name in the deck, you will eventually get 10% 10%10% of the event point (bonus values are calculated by addition, and we round down when calculating the final event point). Please find the maximum event point you will eventually get.

样例输入
1
6
Saaya Power 45000
Kokoro Happy 45000
Kasumi Cool 45000
Rimi Power 45000
Aya Pure 45000
Aya Power 2000
Saaya Tae Kasumi Rimi Arisa
Power
样例输出
382500
题目大意:

每一张卡片有一个名字,颜色,和一个权值,现在有n张卡片,让你选择5张名字不同的卡片,使得总权值和最大,另外,题目给出5种名字(可以加成10%)和一个颜色(可以加成20%),总权值=基础权值(5张卡片权值和)* 总加成之和。

解析

先将卡片按名字分类,每一类最多只能选一个,定义dp[i][j][k]表示前i类加成为j(j/10),且已经选了k张牌时的最大权值,加成最大为150,所以j最大取的15.

#include <bits/stdc++.h>
 
#define x first
#define y second
#define pii pair<int,int>
#define sz(x) (int)(x).size()
#define Max(x,y) (x)>(y)?(x):(y)
#define Min(x,y) (x)<(y)?(x):(y)
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const LL inf = -1e18;
const int maxn = 1e5+15;
struct node{
    char Name[15],Color[15];
    int power;
}A;
char color[15];
LL dp[maxn][16][6];
vector<node> v[maxn];
//v[i]---代表一种名字的一个集合
unordered_map<string,int> mp,np;
//mp--加分的5个名字
//np--那个名字对应v中的那个集合里面。
//cnt表示不同名字个数,
int main(){
    int T,n,cnt = 0;  
    for(int i = 0;i < 16; ++i)
    for(int j = 0;j < 6; ++j)
        dp[0][i][j] = inf;
    dp[0][0][0] = 0; scanf("%d",&T);
    while(T--){
        scanf("%d",&n); mp.clear(), np.clear();
        for(int i = 1;i <= cnt; ++i) v[i].clear();
        cnt = 0;
        for(int i = 1;i <= n; ++i){//将名字与集合对应。
            scanf("%s %s %d",A.Name,A.Color,&A.power);
            if(np.count(A.Name)) v[np[A.Name]].push_back(A);
            else np[A.Name] = ++cnt, v[cnt].push_back(A);
        }
        for(int i = 1;i <= 5; ++i) scanf("%s",color),mp[color] = 1;
        scanf("%s",color);
        for(int i = 1;i <= cnt; ++i){
            for(int j = 0;j < 16; ++j)
                for(int k = 0;k < 6; ++k)
                    dp[i][j][k] = dp[i-1][j][k];
            for(int s = 0;s < sz(v[i]); ++s){
                int w = 0; 
                if(mp.count(v[i][s].Name)) w++;
                if(strcmp(v[i][s].Color,color)==0) w+=2;
                for(int j = w;j < 16; ++j)
                    for(int k = 1;k < 6; ++k)
                        dp[i][j][k] = Max(dp[i][j][k],dp[i-1][j-w][k-1]+v[i][s].power);
            }
        }
        LL ans = 0;
        for(int i = 0;i < 16; ++i){
            if(dp[cnt][i][5] > 0) ans = Max(ans,dp[cnt][i][5]*(10+i)/10);
        }
        cout << ans << '\n';
    }
    return 0;
}
D Easy Problem(莫比乌斯反演+欧拉降幂)

题目链接
在这里插入图片描述

题目大意

我们定义一个序列a1,…an是(n, m. d)–好的(一种规则),当且仅当:

  • ∀1≤i≤n,1≤ai ≤m ,并且
  • gcd(a1,⋯,an)=d
    给定 n,m,d,k 求所有 (n,m,d) -好的序列的“所有元素的积的 k 次幂”的和,对某个合数取模。输入的 n≤10100000,m≤100000,k≤109
    在这里插入图片描述
    最后使用欧拉降幂
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int mod=59964251;
const int phi=59870352;
int mu[N];
int prime[N];
int vis[N];
int cnt;
long long sum[N];
char str[N];
void init()//求mu 
{
    mu[1]=1;
    for(int i=2; i<N; i++)
    {
        if(!vis[i])
        {
            prime[cnt++]=i;
            mu[i]=-1;
        }
        for(int j=0; j<cnt&&i*prime[j]<N; j++)
        {
            vis[prime[j]*i]=1;
            if(i%prime[j]==0)
            {
                mu[i*prime[j]]=0;
                break;
            }
            else
                mu[i*prime[j]]=-mu[i];
        }
    }
}
long long quickmod(long long a,long long b)
{
    long long ans=1;
    while(b)
    {
        if(b%2==1)
            ans=ans*a%mod;
        b=b/2;
        a=a*a%mod;
    }
    return ans;
}
int main()
{
    init();
    int t;
    scanf("%d",&t);
    while(t--)
    {
        long long m,d,k;
        scanf("%s%lld%lld%lld",str,&m,&d,&k);
        m/=d;
        sum[0]=0;
        for(int i=1; i<=m; i++)
            sum[i]=(sum[i-1]+quickmod(i,k))%mod;
        int len=strlen(str);
        int flag=0;
        long long n=0;
        for(int i=0; i<len; i++)
        {
            n=n*10+str[i]-'0';
            n=n%phi;
        }
        long long ans=0;
        for(int i=1; i<=m; i++)
        {
            ans=(ans+mu[i]*quickmod(i,k*n%phi+phi)%mod*quickmod(sum[m/i],n+phi)%mod+mod)%mod;
        }
        ans=ans*quickmod(d,k*n%phi+phi)%mod;
        printf("%lld\n",ans);
    }
    return 0;
}
/*
59870352
*/

/*
1
1000000000 100000 100000 1000000000
38069446
*/


F Function!

在这里插入图片描述
在这里插入图片描述

#include <bits/stdc++.h>
 
#define x first
#define y second
#define pii pair<int,int>
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int maxn = 1e5+15;
const int mod = 998244353;
const int inv2 = 499122177;
const int inv6 = 166374059;
LL qpow(LL a,LL x,LL mod){
    LL res = 1ll;
    while(x){
        if(x&1) res = res*a%mod;
        a = a*a%mod;
        x >>= 1;
    }
    return res;
}
LL per(LL x){
    x %= mod;
    return x*(x+1)%mod*(2*x+1)%mod*inv6%mod;
}
LL cal(LL l,LL r,LL n){
    LL ans = (r-l+1)%mod*((r+l)%mod)%mod*((n+1)%mod)%mod*inv2%mod-per(r)+mod+per(l-1);
    return ans%mod;
}
inline int sqr(LL n){
    LL x = sqrt(n*1.0);
    for(int i = x+2;i >= x-2; --i){
        if(1ll*i*i <= n) return i;
    }
}
LL solve(LL n){
    int k = sqr(n);
    LL ans = 0;
    for(int a = 2;a <= k; ++a){
        LL s = a,cnt = 1;
        while(s*a <= n){
            ans += (s*a-s)%mod*a%mod*cnt%mod;
            if(ans >= mod) ans -= mod;
            cnt++; s = s*a;
        }
        ans += (n-s+1)%mod*cnt%mod*a%mod;
        if(ans >= mod) ans -= mod;
    }
    ans += cal(k+1,n,n);
    return ans%mod;
}
int main(){
    LL n; cin >> n;
    cout << solve(n) << '\n';
    return 0;
}
G. Pot!!

题目链接
在这里插入图片描述

题意:分析:

查询等价于求区间内的每个数唯一分解后,指数的最大值,注意到 x<=10,直接对 2,3,5,7 每个质因子开一颗线段树维护幂的最值,更新操作就相当于区间加

#include <bits/stdc++.h>
 
#define x first
#define y second
#define pii pair<int,int>
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int maxn = 1e5+15;
int prime[4] = {2,3,5,7};
int tr[4][maxn<<2],lz[4][maxn<<2],n,q,l,r,x;
char op[100];
inline void pushdown(int o,int x){
    tr[o][x<<1] += lz[o][x]; tr[o][x<<1|1] += lz[o][x];
    lz[o][x<<1] += lz[o][x]; lz[o][x<<1|1] += lz[o][x];
    lz[o][x] = 0;
}
void updata(int l,int r,int L,int R,int x,int o,int v){
    if(l > R || r < L) return ;
    if(l >= L && r <= R){
        tr[o][x] += v;
        lz[o][x] += v;
        return ;
    }
    if(lz[o][x]) pushdown(o,x);
    int mid = (l+r) >> 1;
    updata(l,mid,L,R,x<<1,o,v);
    updata(mid+1,r,L,R,x<<1|1,o,v);
    tr[o][x] = max(tr[o][x<<1],tr[o][x<<1|1]);
}
void query(int l,int r,int L,int R,int x,int &ans){
    if(l > R || r < L) return ;
    if(l >= L && r <= R){
        for(int i = 0;i < 4; ++i) ans = max(tr[i][x],ans);
        return ;
    }
    for(int i = 0;i < 4; ++i){
        if(lz[i][x]) pushdown(i,x);
    }
    int mid = (l+r) >> 1;
    query(l,mid,L,R,x<<1,ans);
    query(mid+1,r,L,R,x<<1|1,ans);
}
int main(){
    cin >> n >> q;
    for(int i = 1;i <= q; ++i){
        scanf("%s",op);
        if(op[1] == 'U'){
            scanf("%d %d %d",&l,&r,&x);
            for(int i = 0;i < 4; ++i){
                if(x%prime[i] == 0){
                    int cnt = 0;
                    while(x%prime[i] == 0){
                        cnt++; x /= prime[i];
                    }
                    updata(1,n,l,r,1,i,cnt);
                }
            }
        }
        else{
            scanf("%d %d",&l,&r);
            int ans = 0;
            query(1,n,l,r,1,ans);
            printf("ANSWER %d\n",ans);
        }
    }
    return 0;
}
H. Delivery Route

高级spfa

题目链接

在这里插入图片描述
首先,由于存在负权路径,直接Dijkstra显然不可取。考虑使用spfa,又很容易被卡掉。我们观察题目比起裸的单源最短路径有何区别:将无向图的每个联通块缩成点后,形成了一个拓扑图!

于是我们可以先用dfs对所有的联通块染色,按照拓扑排序的顺序,对每个联通块内部用Dijkstra。如果一条边连向另一个联通块,就更新其该点的最短路径(但不加入堆中),并且让目标联通块的入度−1 -1−1。

#include <bits/stdc++.h>
using namespace std;
int n, x, y, s;
const int MAXN = 2e5 + 50;
typedef pair<int, int> pii;
#define mp make_pair

int head[MAXN], tot;
struct Edge
{
	int nex, to, dis;
}eg[MAXN];
void addedge(int a, int b, int c)
{
	eg[++tot] = {head[a], b, c};
	head[a] = tot;
}
void add(int a, int b, int c)
{
	addedge(a, b, c);
	addedge(b, a, c);
}

vector<int> bel[MAXN];
int co[MAXN], cnt;//联通块的颜色、联通块的数量
void dfs(int v, int col)//dfs给联通块染色
{
	co[v] = col;
	bel[col].push_back(v);
	for(int i = head[v]; i; i = eg[i].nex)
	{
		int to = eg[i].to;
		if(!co[to]) dfs(to, col);
	}
}

int deg[MAXN], dis[MAXN];//每个联通块的入度、单源最短路径
int vis[MAXN];
queue<int> que;//拓扑排序:入度为0的联通块入队
priority_queue<pii, vector<pii>, greater<pii> > pq;
void Dijkstra()
{
	memset(dis, 0x3f, sizeof(dis));
	dis[s] = 0;
	for(int i = 1; i <= cnt; i++) if(!deg[i]) que.push(i);
	while(!que.empty())//topological-sort
	{
		int u = que.front(); que.pop();
		int sz = bel[u].size();
		for(int i = 0; i < sz; i++) pq.push(mp(dis[bel[u][i]], bel[u][i]));
		while(!pq.empty())//Dijkstra
		{
			int x = pq.top().second; pq.pop();
			if(vis[x]) continue;
			vis[x] = 1;
			for(int i = head[x]; i; i = eg[i].nex)
			{
				int to = eg[i].to;
				if(co[x] == co[to] && dis[to] > dis[x] + eg[i].dis)
				{
					dis[to] = dis[x] + eg[i].dis;
					pq.push(mp(dis[to], to));
				}
				if(co[x] != co[to])
				{
					dis[to] = min(dis[to], dis[x] + eg[i].dis);
					deg[co[to]]--;
					if(!deg[co[to]]) que.push(co[to]);
				}
			}
		}
	}
}

int main()
{
	scanf("%d%d%d%d", &n, &x, &y, &s);
	for(int i = 1, a, b, c; i <= x; i++)
	{
		scanf("%d%d%d", &a, &b, &c);
		add(a, b, c);
	}
	for(int i = 1; i <= n; i++) if(!co[i]) dfs(i, ++cnt);
	for(int i = 1, a, b, c; i <= y; i++)
	{
		scanf("%d%d%d", &a, &b, &c);
		addedge(a, b, c);
		deg[co[b]]++;
	}
	Dijkstra();
	for(int i = 1; i <=n; i++)
	{
		if(dis[i] > 1e9) puts("NO PATH");
		else printf("%d\n", dis[i]);
	}
	return 0;
}

I. Base62
题目大意

将一个 x 进制下的数字 k 转换到 y 进制下。k≤x120

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1500;
int x, y;
char s[MAXN], ans[MAXN];
int t[MAXN], res[MAXN];

int getnum(char c)//将字符转化为数字
{
    if(c >= '0' && c <= '9') return c - '0';
    if(c >= 'A' && c <= 'Z') return c - 'A' + 10;
    else return c - 'a' + 36;
}
char getch(int num)//将数字转化为字符
{
    if(num <= 9) return num + '0';
    if(num <= 35) return num + 'A' - 10;
    else return num + 'a' - 36;
}

void work()
{
    int len = (int)strlen(s+1);
    for(int i = 1; i <= len; i++) t[i] = getnum(s[i]);
    for(int j = 1, k = 0; j <= len; )//j为当前最高位
    {
        for(int i = j; i < len; i++)
        {
            t[i+1] += t[i] % y * x;
            t[i] /= y;
        }
        res[++k] = t[len] % y;//得到此次除y的余数
        t[len] /= y;
        while(j <= len && !t[j]) j++;
        for(int i = 1; i <= k; i++) ans[i] = getch(res[k-i+1]);
        //倒序输出
    }
}

int main()
{
    scanf("%d%d%s", &x, &y, s+1);
    work();
    printf("%s\n", ans+1);
    return 0;
}

python代码

a = input().split();
x = int(a[0]); y = int(a[1])
z = list(a[2]); z.reverse();
sum = 0; b = 1;
for i in z:
    if i.isdigit():
        sum += int(i)*b
    elif i.islower():
        sum += ((ord(i)-ord('a'))+36)*b
    elif i.isupper():
        sum += ((ord(i)-ord('A'))+10)*b
    b *= x
if sum == 0:
    print(0)
else:
    ans = []
    while sum > 0:
        ans.append(sum%y)
        sum = sum//y
    ans.reverse()
    for i in ans:
        if i < 10:
            print(i,end='')
        elif i < 36:
            print(chr(ord('A')+i-10),end='')
        else:
            print(chr(ord('a')+i-36),end='')
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值