〖补〗: Gym 102411 / ICPC 2019 North-Western Russia

题组链接

M - Managing Difficulties Gym - 102411M

题意:
求满足 a[j]−a[i]=a[k]−a[j]的i,j,k有几组。

思路:
a[j]−a[i]=a[k]−a[j]
可化为: 2×a[j]-a[k])=a]i]

  • 枚举i时:
    • 把i的放到map里
  • 在枚举j,k的时
    • 利用2×a[j]-a[k])=a]i]和
    • map自带的"二分查询":mp.count()匹配
    • 来把枚举i的时间复杂度O(n)降低到O(logn)。
      unordered_map<>等价于无序(不排序)的map容器
      代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        unordered_map<int,int> graph;
        int ans=0;
        int n;
        cin>>n;
        int a[n+1];
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        for(int i=1;i<=n;i++)
        {
            graph[ a[i] ]++;
            for(int k=i+2;k<=n;k++)
            {
		int j=i+1;
		/*因为graph里面含有所有小于等于当前i的a[]数组的数据
		*所有:只要j=i+1,就可以(假)遍历所有小于j的数组
		*/
                if( graph.count(2*a[j]-a[k]) )
                    ans+=graph[ 2*a[j]-a[k] ];
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

J - Just the Last Digit Gym - 102411J

题意:
要求:有一张有向图, 只能从小的点跑向大的点,
input:点a跑到点b的可行方案数的个位数(a<b)
output:让任意两点间是否能直达.
思路:
点a到点b的方法只有:直达或者间接到达 (废话)

故方法:

  1. 枚举求出:所有间接可达的方案总数(具体看代码)
  2. %10后如果与给出的个位数相同, 则无法直达, 反之可以直达.

代码:

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int MAXN = 510;
char mp[MAXN][MAXN];
int res[MAXN][MAXN];
int main(void)
{
	int n; cin >> n;
	for (int i = 1; i <= n; i++) {
		scanf("%s", mp[i] + 1);
		for (int j = 1; j <= n; ++j) {
			res[i][j] = mp[i][j] - '0';
		}
	}
	for (int i = 1; i <= n - 1; ++i) {
		for (int j = i + 1; j <= n; ++j) {
			int temp = 0; //方案数
			for (int k = i + 1; k <= j - 1; ++k) {
				temp += res[i][k] * res[k][j];
			}
			res[i][j] = (temp + 1) % 10 == res[i][j];
		}
	}
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= n; ++j) {
			printf("%d", res[i][j]);
		}
		printf("\n");
	}
	return 0;
}

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+10;
int n,k,a[100009],m;
bool isok(int x)
{
    int ans=0,num=0,j=1;
    /*
    *ans:多少个区间的第k大比mid还大  and 右端点
    *num:统计有多少个数大于当前x
    *j:左端点
    */
    for(int i=1; i<=n; i++)//遍历A数组
    {
        if( a[i]>=x )
        {
            num++;//更新num
        }

        if( num==k )//说明包含当前区间的区间的第k大一定大于等于mid
        {
            ans+=(n-i)+1;//在 i+1 及其之后的区间大于x的点的个数都大于等于k个。
            //右端点的取值
            while( a[j]<x )//移动左端点
                /*
                *如果左端点小于x,则对第k大无影响
                */
            {
                ans+=n-i+1; //在 i+1 及其之后的区间大于x的点的个数都大于等于k个。
                j++;
            }
            num--;
            j++;
        }
    }
    if(ans>=m)	return true;//如果当前x符合题意
    else      return false;
}
signed main()
{
    int t;
    cin >> t;
    while( t-- )
    {
        cin >>n>>k>>m;
        int l=1e9+1,r=0;
        for(int i=1; i<=n; i++)
        {
            cin >> a[i];
            l = min(l,a[i]);
            r = max(r,a[i]);
        }
        int ans=0;
        while( r>=l )
        {
            int mid =(l+r)/2;
            //  cout <<mid <<endl;
            if( isok(mid) )//如果当前x符合题意
            {
                l=mid+1;//二分缩短区间
                ans=mid;
            }
            else
            {
                r=mid-1;//二分缩短区间
            }
        }
        cout << ans <<endl;
    }
}
posted @ 2021-09-18 11:06  kingwzun  阅读(32)  评论(0编辑  收藏  举报