杭电多校总结2021-08-17

1002 Just another board game

Problem Description
“So now I move the piece to (179,231). It’s the 999999999th move of
this game. Finally, one move to go!” “What? Isn’t it only the
999999997th move of this game?” “Oh, f**k.”

After playing some games of Go, Roundgod and kimoyami decide to try
something different. Now they are playing a new kind of game on a
chessboard. The chessboard is a grid board with n rows and m columns.
We assume that the upper left corner of the chessboard has coordinate
(1,1), and the lower right corner of the chessboard has coordinate
(n,m). There’s a number on every grid of the board, with the number
written on the grid on the ith row and jth column equal to aij. What’s
more, there’s a chess piece on the upper left corner(i.e., (1,1)) of
the chessboard initially. Now the two players take turns to choose one
of the following operations, starting from Roundgod: Move the chess
piece. If it’s Roundgod’s turn, he can move the chess piece to any
position in the same row(It’s also OK to move it to the current
position, i.e., not moving it at all). If it’s kimoyami’s turn, he can
move the chess piece to any position in the same column. (It’s also OK
to move it to the current position, i.e., not moving it at all)

Screw it. I’m going home. Finish the game immediately.

The game ends when either of the two players chooses the second
operation or when the game has already been going on for k turns.
(Either of the two players’ operations counts as one turn). The value
of the game is defined as the number on the grid where the chess piece
lands when the game ends. Now, Roundgod wants to maximize this value,
while kimoyami wants to minimize this value. They don’t have the
patience to actually play this game for possibly that many turns, so
they want you to calculate what will be the final value of the game if
both players choose the optimal strategy?

Input
The first line contains a number T(1≤T≤25), denoting the number of
test cases.

The first line of each test case contains three integers
n,m,k(n,m≥1,1≤n×m≤105,1≤k≤1018), denoting the size of the chessboard
and the maximum number of turns the game will last, respectively.

Then n lines follow, the ith(1≤i≤n) of the n lines contain m integers
ai1,ai2,…,aim, where aij(0≤aij≤109) denotes the he number written on
the grid on the ith row and jth column.

It is guaranteed that ∑(n×m)≤106 over all test cases.

Output
For each test case, output one integer in a line, denoting the final value of the game if both players choose the optimal strategy.

大意是两个人下棋,棋盘的每个格子上有一个数字。先手可以同一行左右移动(可以不动),要让停留的数字最大;后手可以同一列上下移动(可以不动),要让停留的数字最小。轮到任何一方走时,他可以选择直接结束游戏,走k步游戏也会结束。
当k=1,只能有第一个操作一次,结果即为第一行上最大的数。

当k为偶数,当k>2时的结果是等价与k=2的。以k=4为例,每个人都可以选择随时停止游戏,当k=4对先手更为有利时,后手会选择在k=2时直接结束游戏,反之,对后手更有利的时候,先手会在k=1时候结束游戏,因此,只需要考虑k=2的时候即可。 当k=2,假设初始位置为s[x][0];那么有两种情况,要么,都不操作,结果即为s[x][0],要么先手会选择棋盘上列最小值最大的一列(后手必定会操作使得结果停留在那一列的最小值上),因此需要输出结果max(s[x][0],max(每列最小值))。

当k为奇数的时候,等价于k=3,原理同上。当k=3的时候,有三种情况,先手直接结束,后手直接结束和走完三步(最后一个人直接结束一定没有走完三步更优,因此一定不会有这种情况发生),假设开始的位置为s[x][0],直接结束为s[x][0],后手直接结束会停在这一行的最大值上,否则,会停在每一行的最大值最小的那个数上(后手没有结束游戏的时候,最后先手一定会操作,所以一定会停在一个行最大值上,因此后手必定会选择行最大值最小的一列),我们发现第二种和第三种情况都会停在行最大值上,因此第三种无疑是最优解决方案,直接考虑第一种与第三种即可,即输出max(s[x][0],max(行最大值最小的列的行最大值))

#include<bits/stdc++.h>
#define ll long long
#define cer(x) cerr<<(#x)<<" = "<<(x)<<'\n'
using namespace std;
const int N=1e6+10;
ll n,m,k;
ll min1[N];
ll maxline[N];
vector<ll> v[N];
 
void init(){
	for(int i=1;i<=n;i++){
		v[i].clear();
	}
}
 
int main(){ 
	ios::sync_with_stdio(0); cin.tie(0);
	int t; cin>>t;
	while(t--){
		cin>>n>>m>>k;
		
		init();
		
		ll temp;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				cin>>temp;
				v[i].push_back(temp);
			}
		}
		
		ll max1=0;
		for(int i=0;i<m;i++){
			max1=max(max1,v[1][i]);
		}
		
		for(int i=1;i<=m;i++){
			min1[i]=1e9+10; // inf
		}
		for(int i=0;i<m;i++){
			for(int j=1;j<=n;j++){
				min1[i+1]=min(min1[i+1],v[j][i]);
			}
		}
		for(int i=1;i<=m;i++){
			maxline[i]=0;
		}
		for(int i=1;i<=n;i++){
			for(int j=0;j<m;j++){
				maxline[i]=max(maxline[i],v[i][j]);
			}
		}
		
		if(k==1){
			cout<<max1<<endl;
			continue;
		}
		
		// k>=2
		if(k%2==0){ // 相当于k==2 
			ll maxx=0;
			for(int i=2;i<=m;i++){
				maxx=max(maxx,min1[i]);
			}
			cout<<max(v[1][0],maxx)<<endl;
		}
		else{ // k>=3,奇数 
			ll minn=1e9+10;
			for(int i=1;i<=n;i++){
				minn=min(minn,maxline[i]);
			}
			cout<<max(v[1][0],minn)<<endl;
		}
	}
	return 0;
}

1003 Dota2 Pro Circuit

Problem Description
TI10 and ICPC World Finals 2020, which will be held earlier? Take a
bet!

The International(TI) is the biggest and most prestigious event of
Dota2 and is commonly held annually in August. The Dota2 teams should
try to earn points to be eligible to compete in TI. There are two ways
of earning points, first is by competing in the regional contests,
second is by competing in the tournaments, where all teams are
gathered to compete together and earn points according to their rank
in the tournament. A team’s final score is the sum of scores from both
the regional contests and tournaments.

Now that the regional contests have finished, there are n teams taking
part, and the ith team has earned ai points from the regional
contests. Also, the team that gets the ith rank in the tournament can
gain bi points.

cyz is a huge fan of Dota2. So before the tournament starts, he will
predict the final rank of all teams. cyz wants to know, for each team,
what’s its best possible rank and its worst possible rank after the
tournament finishes.

If a team has a final score equal to x, its rank is defined as one
plus the number of teams with a strict higher score than it. For
example, if the final score of four teams are 700,500,500,300
respectively, then their final ranks are 1,2,2,4, respectively.

Input
The first line contains a number T(1≤T≤20), denoting the number of
test cases.

The first line of each test case contains one number n(1≤n≤5000),
denoting the number of different teams that participate in the
regional contests and tournaments.

The next line contains n integers a1,a2,…,an(0≤ai≤109), denoting the
points of each team before the tournament starts.

Then follows one line containing n integers
b1,b2,…,bn(0≤bn≤bn−1≤…≤b1≤109), where bi denotes the number of
points a team would get if ranking ith in the tournament.

It is guaranteed that there are at most 8 cases with n>100.

Output
For each test case, output n lines, where the ith line contains two
integers besti,worsti(1≤besti≤worsti≤n), denoting the best possible
and worst possible rank a team would get after the tournament
finishes.

大意是给定每个队伍之前的得分ai,下一场可能得到的分数bi,求每一个队伍最好的与最坏的名次。
最高排名(让它下面的人最多),将它本身加上最大的b分,总分记为sc,剩余的a中从小到大枚举,让它们尽可能拿走最大的b分且总分小于等于sc分,到b中分数已经被枚举到了最后一个的时候就break了,a中剩下的个数就是排名在这个对象上面的了。
最低排名(让它上面的人最多),将它本身加上最小的b分,总分记为sc,剩余的a中从大到小枚举,如果本身就比sc还大,说明加什么都会比sc大,直接让这个对象上面的人数 ++ 即可。

#include<bits/stdc++.h>
using namespace std;
int t,n;
struct node{
    long long num,val;
}poi[5001]; 
int ans[5001][2];
long long b[5001];
 
//从小到大进行排序 
bool cmp(node a1,node a2)
{
    return a1.val<a2.val;
}
 
//输入并排序 
void init()
{
 
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
	    scanf("%lld",&poi[i].val);
	    poi[i].num=i;
    }
    for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
    sort(poi+1,poi+1+n,cmp);
    return;
}
 
//求最好的名次   尽量使得其他队伍分数更低 最大的分数给目前最小的队伍 
int maxn(int k)
{	//求解第k个队伍  最好得分是加上最大值 
    int N=poi[k].val+b[1],cnt=0;
	
	//双指针 p指向排序后的a数组 q指向b数组 
    int p=1;	
	int q=1;
	//当循环没有走完的时候 
	while(p<=n && q<=n){
		if(p==k){
			//第k个不必再加 跳过 
			p++;
			continue;
		} 
		//目前队伍得分比k小的时候 cnt++记录一共有多少更小的 
		if(poi[p].val+b[q]<=N){
			p++;
			cnt++;
		}
		q++;
	}
    //减去比自己分数少的队伍就是自己的名次 
    return n-cnt;
}
 
//求最差的名次 
int minn(int k)
{	
    int d=1,N=poi[k].val+b[n],ans=0;
    //ans超过当前的个数,N为队伍最少可以得分 
 
    for(int i=1;i<=n;i++){
        if(i==k) continue;
        if(poi[i].val>N){
        //如果不用加分数就已经超过一定在它前面 
            ans++;
            continue;
        } 
        else
        {	//加上更大 消耗一个d,d++ 
            if(poi[i].val+b[d]>N){
                ans++;
                d++;
            }
        }
    }
    return ans+1;
}
 
int main() // AC
{
    scanf("%d",&t);
    while(t>0)
    {
        t--;
        init();
        for(int i=1;i<=n;i++){
        	int number=poi[i].num;
        	ans[number][0]=maxn(i);
        	ans[number][1]=minn(i);
        }
        for(int i=1;i<=n;i++){
            printf("%d %d\n",ans[i][0],ans[i][1]);
        }
    }
    return 0;
 }

1007 Boring data structure problem

Problem Description
“Why don’t you write some background story for this boring data
structure problem?” “Because it’s too boring…”

There’s a queue(not the original queue one may refer to as a data
structure, here a double-ended queue, to be more precise) that is
initially empty. Then, there are infinite elements numbered 1,2,…,
entering the queue in the order of their numbers(i.e., the first
element to enter the queue is numbered 1, the second, 2 et cetera). If
an element leaves the queue, it will not enter the queue anymore.

Now there are q operations, each in one of the following forms: Let
the next element enter the left end of the queue.

Let the next element enter the right end of the queue.

Let the element numbered x leave the queue.

Ask the number of the element in the middle of the queue. (If there
are m elements in the queue currently, you should output the number of
the element that is the ⌈m+12⌉th from the left).

Input
The first line of each test case contains one number q(1≤q≤107),
denoting the number of operations.

Then q lines follow, each in one of the following forms: L, denoting
the next element enters the left end of the queue

R, denoting the next element enters the right end of the queue

G x, denoting the element numbered x leaves the queue (It is
guaranteed that the element numbered x is in the queue when this
operation is applied)

Q denoting a query that asks the number of the element in the middle
of the queue(It is guaranteed that the queue is not empty when this
operation is applied)

It is guaranteed that the number of operations of the third and the
fourth type is both less than 1.5⋅106.

Output
For each operation of the fourth kind, output a number in a line, denoting the answer to the query.

比较简单的链表模拟,写之前应该先思考一下怎么写比较简单,有时候换一种写法可以少很多特判和代码量。

置左右端两个空指针和MID,执行操作时动态维护即可。

#include <bits/stdc++.h>
using namespace std;

int x;
char s[2];
const int INF = 1e9 + 7;
const int N = 2e7 + 10000;
int L[N], R[N], a[N], p[N];
//L[i] R[i]记录i的左右结点地址  a[i]记录地址i的值 p[i]记录值i的地址 a-p对称
int cnt, pos_l, pos_r, n, mid, beg;
// pos_l pos_r 表示两端的空指针 mid为答案所在的地址 cnt为下一个值 n为容器中的数的个数
void init()
{
    n = 0;
    pos_l = beg;
    pos_r = beg + 1;
    L[pos_l] = -INF;
    R[pos_l] = pos_r;
    L[pos_r] = pos_l;
    R[pos_r] = INF;
    mid = pos_r;
}

void f1()
{
    if (n == 1) //删空了
    {
        init();
        return;
    }
    x = p[x];
    if (n & 1)
    {
        if (x <= mid)
            mid = R[mid];
    }
    else
    {
        if (x >= mid)
            mid = L[mid];
    }
    R[L[x]] = R[x];
    L[R[x]] = L[x];
    --n;
}

void f2() //L
{
    ++cnt;
    ++n;
    L[R[pos_l]] = pos_l;
    L[pos_l] = pos_l - 1;
    L[pos_l - 1] = -INF;
    R[pos_l - 1] = pos_l;
    a[pos_l] = cnt;
    p[cnt] = pos_l;
    pos_l--;
    if (n % 2 == 1)
        mid = L[mid];
}

void f3() //R
{
    ++cnt;
    ++n;
    R[L[pos_r]] = pos_r;
    R[pos_r] = pos_r + 1;
    R[pos_r + 1] = INF;
    L[pos_r + 1] = pos_r;
    a[pos_r] = cnt;
    p[cnt] = pos_r;
    pos_r++;
    if (n % 2 == 0)
        mid = R[mid];
}

void f4()
{
    printf("%d\n", a[mid]);
}

void solve()
{
    int q;
    scanf("%d", &q);
    beg = 1e7 + 5;
    init();
    for (int i = 0; i < q; i++)
    {
        scanf("%s", s);
        if (s[0] == 'G')
        {
            scanf("%d", &x);
            f1();
        }
        else if (s[0] == 'L')
            f2();
        else if (s[0] == 'R')
            f3();
        else
            f4();
    }
}

int main()
{
    solve();
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值