zoukankan      html  css  js  c++  java
  • 2019 ICPC Asia Yinchuan Regional

    A. Girls Band Party

    大意:

    给出n张牌,每张牌都有花色和名字,以及价值,只能选择不同名字的五张牌,有5种奖励名字和一种奖励花色,和奖励名字相同的名字可以提供10%的加成,相同的颜色可以提供20%的加成,这些加成可以累加到最后一起结算,问最大的价值和是多少

    思路:

    分组背包问题,但是需要加一个维度记录加成的等级,这样最后再计算价值和即可

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e5 + 5;
    typedef long long LL;
    int t, n,level[N],dp[N][10][20];
    struct node{
        string name;
        string color;
        int val;
    } a[N];
    unordered_map<string, int> mp;
    vector<int> g[N];
    int main(){
        cin>>t;
        for(int j = 0;j < 6; ++j)
            for(int i = 0;i < 16; ++i)
                dp[0][j][i] = -0x3f3f3f3f;
        while(t--){
            cin>>n;
            mp.clear();
            int idx = 1;
            for (int i = 1; i <= n;i++){
                cin >> a[i].name >> a[i].color >> a[i].val;
                level[i] = 0;
                if(mp[a[i].name]==0){
                    g[idx].clear();
                    mp[a[i].name] = idx++;
                }
                g[mp[a[i].name]].push_back(i);
            }
            for (int i = 1; i <= 5;i++){
                string name;
                cin >> name;
                for (int j = 1; j <= n;j++){
                    if(a[j].name==name){
                        level[j] += 1;
                    }
                }
            }
            string color;
            cin >> color;
            for (int i = 1; i <= n;i++){
                if(color==a[i].color){
                    level[i] += 2;
                }
            }
            dp[0][0][0] = 0;
            for (int i = 1; i < idx;i++){
                for (int j = 1; j <= 5;j++){
                    for (int k = 0; k <= 15;k++){
                        dp[i][j][k] = dp[i - 1][j][k];
                    }
                }
                for (int p = 0; p < g[i].size(); p++){
                    for (int j = 1; j <= 5; j++){
                        for (int k = level[g[i][p]]; k <= 15; k++){
                            dp[i][j][k] = max(dp[i][j][k], dp[i - 1][j - 1][k - level[g[i][p]]] + a[g[i][p]].val);
                        }
                    }
                }  
            }
            int res = 0;
            for (int i = 0; i <= 15;i++){
                res = max(res, dp[idx-1][5][i] + dp[idx-1][5][i] * i / 10);
            }
            cout << res << endl;
        }
        return 0;
    }
    

    B. So Easy

    大意:

    初始是一个全为0的数组,每次操作都是将一列或者一行全加1,最后选择一个数将其置为-1,现在给出最后的结果,问-1的位置在置为-1前是多少

    思路:

    因为数据量很小,直接暴力每一行和每一列,都减去该列(行)最小的数字,最后看-1的位置是多少

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e3 + 5;
    int n,mp[N][N],x,y;
    int main(){
        cin >> n;
        for (int i = 0; i < n;i++){
            for (int j = 0; j < n;j++){
                cin >> mp[i][j];
                if(mp[i][j]==-1){
                    x = i;
                    y = j;
                }
            }
        }
        for (int i = 0; i < n;i++){
            int minn = 0x3f3f3f3f;
            for (int j = 0; j < n;j++){
                if(minn>mp[i][j]&&(!(i==x&&j==y))){
                    minn = mp[i][j];
                }
            }
            for (int j = 0; j < n;j++){
                mp[i][j] -= minn;
            }
        }
        for (int j = 0; j < n;j++){
            int minn = 0x3f3f3f3f;
            for (int i = 0; i < n;i++){
                if(minn>mp[i][j]&&(!(i==x&&j==y))){
                    minn = mp[i][j];
                }
            }
            for (int i = 0; i < n;i++){
                mp[i][j] -= minn;
            }
        }
        cout << -(mp[x][y] + 1) << endl;
        return 0;
    }
    

    C. Image Processing

    咕咕咕

    D. Easy Problem

    咕咕咕

    E. XOR Tree

    咕咕咕

    F. Function!

    大意:

    图片.png

    (图源:https://blog.csdn.net/FSAHFGSADHSAKNDAS/article/details/106162905)

    思路:

    (a<=sqrt{n})时直接跳着做,否则直接O(1)的复杂度求出解

    需要注意的是,因为n很大,当他去乘别的数的时候要先取模再乘,不能乘完再取模,否则就wa了....

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e6 + 5;
    typedef long long LL;
    LL n, res, inv2, inv6;
    const LL mod = 998244353;
    
    LL qmi(LL a, LL k, LL p)
    {
        LL res = 1 % p; // res记录答案, 模上p是为了防止k为0,p为1的特殊情况
        while (k)
        { // 只要还有剩下位数
            if (k & 1)
                res = (LL)res * a % p; // 判断最后一位是否为1,如果为1就乘上a,模上p, 乘法时可能爆int,所以变成long long
            k >>= 1;                   // 右移一位
            a = (LL)a * a % p;         // 当前a等于上一次的a平方,取模,平方时可能爆int,所以变成long long
        }
        return res;
    }
    
    LL s1(LL k)
    {
        k = k % mod;
        return k * (k + 1) % mod * inv2 % mod;
    }
    
    LL s2(LL k)
    { //平方和
        return k %= mod, k * (k + 1) % mod * (k * 2 + 1) % mod * inv6 % mod;
    }
    
    int main()
    {
        cin >> n;
        inv2 = qmi(2, mod - 2, mod), inv6 = qmi(6, mod - 2, mod);
        LL a;
        for (a = 2; a * a <= n; a++)
        {
            LL sum = a - 1;
            LL pre = a, now = a * a;
            LL t=1;
            for (LL i = 2; i <=  n; i++)
            {
                
                if (sum + now - pre > n)
                {
                    res = (res + a * (n - sum) % mod * (i - 1) % mod) % mod;
                    break;
                }
                res = (res + a * (i - 1) % mod * (now - pre) % mod) % mod;
                sum += now - pre;
                pre = now;
                now = now * a;
            }
        }
        res = (res + ((n + 1) % mod * (s1(n) - s1(a - 1) + mod) % mod) - (s2(n) - s2(a - 1) + mod) % mod + mod) % mod;                                             
        cout << res << endl;
        return 0;
    }
    

    G. Pot!!

    大意:

    一个数组初始均为1,有两个操作,一个是修改:将区间([l,r])的值乘上(x,(1<=x<=10))

    另一个是查询:查询区间([l,r])上的数,能被2,3,5,7整除次数的最大值。

    思路:

    直接线段树维护被2 3 5 7 整除的次数最大值以及全部的最大值即可

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e5 + 5;
    typedef long long LL;
    
    struct node{
        int sum2, sum3, sum5, sum7, maxn,lazy2,lazy3,lazy5,lazy7;
    }dat[N << 2];
    int a[N], n, m,prime[4]={2,3,5,7};
    
    // 上传标记,每次左右子树建树/区间修改完都需要上传
    void pushup(int rt) {
        dat[rt].sum2 = max(dat[rt << 1].sum2, dat[rt << 1 | 1].sum2);
        dat[rt].sum3 = max(dat[rt << 1].sum3, dat[rt << 1 | 1].sum3);
        dat[rt].sum5 = max(dat[rt << 1].sum5, dat[rt << 1 | 1].sum5);
        dat[rt].sum7 = max(dat[rt << 1].sum7, dat[rt << 1 | 1].sum7);
        dat[rt].maxn = max(max(dat[rt].sum2, dat[rt].sum3), max(dat[rt].sum5, dat[rt].sum7));
    }
    
    // 建树
    void build(int rt, int l, int r) {
        if (l == r) {  // 递归到叶节点
            dat[rt].sum2 = dat[rt].sum3 = dat[rt].sum5 = dat[rt].sum7 = 0;
            dat[rt].lazy2 = dat[rt].lazy3 = dat[rt].lazy5 = dat[rt].lazy7 = 0;
            dat[rt].maxn = 0;
            return;
        }
        // 递归建立左右子树
        int mid = (l + r) >> 1;
        build(rt << 1, l, mid);  
        build(rt << 1 | 1, mid + 1, r);
        pushup(rt);  // 上传
    }
    
    // 下传,下传标记,同时改变dat数组
    void pushdown(int rt, int l, int r) {
        if (dat[rt].lazy2) {
            dat[rt << 1].lazy2 += dat[rt].lazy2;
            dat[rt << 1 | 1].lazy2 += dat[rt].lazy2;
            dat[rt << 1].sum2 += dat[rt].lazy2;
            dat[rt << 1 | 1].sum2 += dat[rt].lazy2;
            dat[rt].lazy2 = 0;
        }
        if (dat[rt].lazy3) {
            dat[rt << 1].lazy3 += dat[rt].lazy3;
            dat[rt << 1 | 1].lazy3 += dat[rt].lazy3;
            dat[rt << 1].sum3 += dat[rt].lazy3;
            dat[rt << 1 | 1].sum3 += dat[rt].lazy3;
            dat[rt].lazy3 = 0;
        }
        if (dat[rt].lazy5) {
            dat[rt << 1].lazy5 += dat[rt].lazy5;
            dat[rt << 1 | 1].lazy5 += dat[rt].lazy5;
            dat[rt << 1].sum5 += dat[rt].lazy5;
            dat[rt << 1 | 1].sum5 += dat[rt].lazy5;
            dat[rt].lazy5 = 0;
        }
        if (dat[rt].lazy7) {
            dat[rt << 1].lazy7 += dat[rt].lazy7;
            dat[rt << 1 | 1].lazy7 += dat[rt].lazy7;
            dat[rt << 1].sum7 += dat[rt].lazy7;
            dat[rt << 1 | 1].sum7 += dat[rt].lazy7;
            dat[rt].lazy7 = 0;
        }
        dat[rt << 1].maxn = max(max(dat[rt << 1].sum2, dat[rt << 1].sum3), max(dat[rt << 1].sum5, dat[rt << 1].sum7));
        dat[rt << 1 | 1].maxn = max(max(dat[rt << 1 | 1].sum2, dat[rt << 1 | 1].sum3), max(dat[rt << 1 | 1].sum5, dat[rt << 1 | 1].sum7));
        return;
    }
    
    // 区间修改: [L, R] += x
    void modify(int rt, int l, int r, int L, int R, int x2,int x3,int x5,int x7) {
        if (L <= l && r <= R) {  // 如果当前区间被完全包含
            dat[rt].sum2 += x2;
            dat[rt].lazy2 += x2;
            dat[rt].sum3 += x3;
            dat[rt].lazy3 += x3;
            dat[rt].sum5 += x5;
            dat[rt].lazy5 += x5;
            dat[rt].sum7 += x7;
            dat[rt].lazy7 += x7;
            return ;
        }
        
        pushdown(rt, l, r);  // 下传
        // 递归左右子树修改区间
        int mid = (l + r) >> 1;
        if (L <= mid)
            modify(rt << 1, l, mid, L, R, x2, x3, x5, x7);
        if (mid < R) modify(rt << 1 | 1, mid + 1, r, L, R, x2, x3, x5, x7);
        pushup(rt);  // 上传
        return;
    }
    
    // 区间查询:获得[L, R]的区间和
    LL query(int rt, int l, int r, int L, int R) {
        if (L <= l && r <= R) return dat[rt].maxn;  // 如果[l, r]被完全包含于[L, R]
        pushdown(rt, l, r);  // 标记下传
        // 递归加上左右子树
        int mid = (l + r) >> 1;
        LL res = 0;
        if (L <= mid) res = max(res,query(rt << 1, l, mid, L, R));
        if (mid < R) res = max(res,query(rt << 1 | 1, mid + 1, r, L, R));
        return res;
    }
    
    int main() {
        cin >> n >> m;
        for (int i = 1; i <= n; ++i) a[i]=1;  // 读入数组
        build(1, 1, n);  // 建树
        char op[10];
        for (int i = 1, a, b, x; i <= m; ++i) {
            scanf("%s", &op);
            if (strcmp(op,"MULTIPLY")==0) {
                scanf("%d%d%d", &a, &b, &x);
                int xn[4] = {0, 0, 0, 0};
                for (int j = 0; j < 4;j++){
                    while(x%prime[j]==0){
                        xn[j]++;
                        x /= prime[j];
                    }
                }
                modify(1, 1, n, a, b, xn[0], xn[1], xn[2], xn[3]); // 区间修改
            }
            else {
                scanf("%d%d", &a, &b);
                printf("ANSWER %lld
    ", query(1, 1, n, a, b));  // 区间查询
            }
        }
        return 0;
    }
    

    H. Delivery Route

    大意:

    在一张既有有向边又有无向边的图上求出每个点到源点的最短距离,保证有向边不会形成有向环

    思路:

    先把无向边建的图分成很多连通块,缩成超级点。再加上有向边后,整张图就变成一张dag。然后拓扑排序求最短路,当处理超级点时,用堆优化版dijkstra处理

    #include<bits/stdc++.h>
     
    using namespace std;
    
    typedef pair<int, int> PII;
    int const N = 3e4 + 10, M = 2e5 + 10;
    int e[M], ne[M], w[M], idx, h[M];
    int t, r, p, source;
    int id[N];
    vector<int> block[N];
    int bcnt;
    int st[N];
    int dis[N];
    queue<int> q;
    int din[N];
    
    void add(int a, int b, int c) {
        e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
    }
    
    // 按照连通块缩点
    void dfs(int u, int cnt) {
        id[u] = cnt;
        block[cnt].push_back(u);
        for (int i = h[u]; i != -1; i = ne[i]) {
            int j = e[i];
            if (!id[j]) dfs(j, cnt);
        }
    }
    
    // 堆优化版dijkstra算法求连通块内的最短路
    void dijkstra(int bid) { 
        priority_queue<PII, vector<PII>, greater<PII> >heap;
        for (auto b: block[bid]) heap.push({dis[b], b});
        while (heap.size()) {
            auto t = heap.top();
            heap.pop();
            int distance = t.first, ver = t.second;
            if (st[ver]) continue;
            st[ver] = 1;
            for (int i = h[ver]; i != -1; i = ne[i]) {
                int j = e[i];
                if (dis[j] > distance + w[i]) {
                    dis[j] = distance + w[i];
                    if (id[ver] == id[j]) heap.push({dis[j], j});  // 如果是一个超级点内才能更新
                }
                if (id[ver] != id[j]) { // 不在一个超级点内,那么需要按照拓扑排序逻辑更新
                    din[id[j]]--;
                    if (!din[id[j]]) q.push(id[j]);
                }
            }
        }
    }
    
    // 拓扑排序求最短路
    void top_sort() {
        for (int i = 1; i <= bcnt; ++i)
            if (!din[i]) q.push(i);
        memset(dis, 0x3f, sizeof dis);
        memset(st, 0, sizeof st);
        dis[source] = 0;
        while (q.size()) {
            auto t = q.front();
            q.pop();
            dijkstra(t);
        }
    }
     
    int main() {
        memset(h, -1, sizeof h);
        cin >> t >> r >> p >> source;
        for (int i = 1; i <= r; ++i) {  // 读入无向边
            int a, b, c;
            scanf("%d %d %d", &a, &b, &c);
            add(a, b, c);
            add(b, a, c);
        }
        for (int i = 1; i <= t; ++i) {  // 把所有点按照连通块划分为超级点
            if (!id[i]) dfs(i, ++bcnt);
        }
        for (int i = 1; i <= p; ++i) {  // 读入有向边
            int a, b, c;
            scanf("%d %d %d", &a, &b, &c);
            add(a, b, c);
            din[id[b]]++;
        }
        top_sort();  // 拓扑排序求最短路
        for (int i = 1; i <= t; ++i) {  // 判断是否有最短路
            if (dis[i] > 0x3f3f3f3f / 2) cout << "NO PATH
    ";
            else cout << dis[i] << endl;
        }
        return 0;
    }
    

    I. Base62

    大意:

    给出一个字符串,要求从n进制转化为m进制

    思路:

    短除法模板题

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e3 + 5;
    typedef long long LL;
    
    int n,m;
    char str1[N],str2[N];
    int t[N],ans[N];
    
    int getnum(char ch) //字符转数字
    {
        if(ch <= '9') return ch-'0';
        else if(ch <= 'Z') return 10+ch-'A';
        else return 36+ch-'a';
    }
    
    char getch(int num) //数字转字符
    {
        if(num <= 9) return num+'0';
        else if(num <= 35) return num-10+'A';
        else return num-36+'a';
    }
    
    void solve()
    {
        int len = strlen(str1);
    
        for(int i = 0; i < len; i++)    //先把字符串变成数组,高位->低位
            t[i] = getnum(str1[i]);
        
        int j = 0,k = 0;
        while(j < len)
        {
            for(int i = j; i < len-1; i++)  //除以m,把余数加到下一位
            {
                t[i+1] += t[i] % m * n;
                t[i] /= m;
            }
            
            ans[k++] = t[len-1]%m;      //个位数余m
            t[len-1] /= m;
    
            while(j < len && !t[j]) j++;    //最高位是0,j++
        }
    
        for(int i = 0; i < k; i++)  //逆序变成字符串
        {
            str2[i] = getch(ans[k-i-1]);
        }
    }
    
    int main()
    {
        int t;
        memset(ans, 0, sizeof(ans));
        memset(str2, 0, sizeof(str2));
        cin>>n>>m;
        cin>>str1;
        solve();
        cout<<str2<<endl;
        
    }
    

    J. Toad’s Travel

    咕咕咕

    K. Largest Common Submatrix

    大意:

    给两个(n*m)的矩阵,矩阵的元素为1~(n*m)的全排列,求两个矩阵的最大相同子矩形的面积(元素数)

    思路:

    由于两个矩阵都是全排列,可以先标记出a矩阵中的元素在b矩阵中出现的位置,然后预处理一下a矩阵中元素向右能延伸多少,标记为l数组,然后就可以转化为经典的单调栈处理最大矩形的问题(只不过这个是旋转了90度的),还需要注意的是,需要保证向下也是能延伸的,所以还需要标记一个l2数组

    #include<bits/stdc++.h>
    using namespace std;
      
    typedef long long LL;
      
    const int N=1e3+100; 
    int l[N][N],l2[N][N],a[N][N],b[N][N];
    struct Node{
    	int h,w;
    	Node(int H,int W){
    		h=H;
    		w=W;
    	}
    };
    
    unordered_map<int, pair<int, int>> pos;//map超时
    
    int main()
    {
    	int n,m;
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                scanf("%d",&a[i][j]);
                l[i][j] = 1;
                l2[i][j]=1;
            }
    			
        }
    	for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
    			scanf("%d",&b[i][j]);
                pos[b[i][j]] = {i, j};
            }
        }		
    	for(int i=1;i<=n;i++){
            for(int j=m-1;j>=1;j--)
    		{
    			int x1=i,y1=j;
    			int x2=pos[a[i][j]].first;
    			int y2=pos[a[i][j]].second;
    			if(a[x1][y1+1]==b[x2][y2+1])
    				l[x1][y1]=l[x1][y1+1]+1;
    		}
        }	
    	for(int i=n-1;i>=1;i--){
            for(int j=1;j<=m;j++){
    			int x1=i,y1=j;
    			int x2=pos[a[i][j]].first;
    			int y2=pos[a[i][j]].second;
    			if(a[x1+1][y1]==b[x2+1][y2])
    				l2[x1][y1]=l2[x1+1][y1]+1;	
    		}
        }
    		
    	int ans=0;
    	for(int i=1;i<=m;i++){
    		int mark=1; 
    		while(mark<=n){
    			stack<Node>st;
    			for(int j=mark;j<=mark+l2[mark][i]-1;j++){
    				int w=0;
    				while(st.size()&&st.top().h>l[j][i]){
    					w+=st.top().w;
    					ans=max(ans,w*st.top().h);
    					st.pop();
    				}
    				st.push(Node(l[j][i],w+1));
    			}
    			int w=0;
    			while(st.size()){
    				w+=st.top().w;
    				ans=max(ans,w*st.top().h);
    				st.pop();
    			}
    			mark+=l2[mark][i];
    		}
    	} 
    	cout<<ans<<endl; 
        return 0;
    }
    

    L. Xian Xiang

    咕咕咕

    M. Crazy Cake

    咕咕咕

    N. Fibonacci Sequence

    签到,输出前五个斐波那契数

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e6 + 5;
    
    int main(){
        cout << "1 1 2 3 5" << endl;
        return 0;
    }
    
  • 相关阅读:
    Java中的System类
    关于Java IO流学习总结
    Java InputStream、String、File相互转化
    Java 里把 InputStream 转换成 String 的几种方法
    详细讲解JAVA中的IO流
    获取 request 中 json 数据
    oracle count 百万级 分页查询记要总数、总条数优化
    ORACLE分页SQL语句
    ORACLE中用rownum分页并排序的SQL语句
    day7
  • 原文地址:https://www.cnblogs.com/dyhaohaoxuexi/p/14065966.html
Copyright © 2011-2022 走看看