A. Groundhog and 2-Power Representation

题意

一个数可以由2x1+2x2+2x3+.....组成,例如1315=210+28+25+2+1=2(2(2+2(0))+2)+2(2(2+2(0)))+2(2(2)+2(0))+2+2(0).
现在给出一个由2的次幂组成的表达式,输出这个数是多少。

题解

不得不说py真的强,看到不到2分钟就有人a了,还以为是原题,赛后发现py三行。(暴风哭泣
我队用的c++高精度模拟写的,队友%%%

代码

py
n = input()
n = n.replace("(","**(")
print(eval(n))
c++
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int maxn = 3e4+10;

vector<int> mult(vector<int>x,vector<int>y)//高精*高精
{
	int xlen=(int)x.size(),ylen=(int)y.size(),zlen=xlen+ylen;
	vector<int>z(zlen);
	for(register int i=0;i<xlen;i++)
		for(register int j=0;j<ylen;j++)
			z[i+j]+=x[i]*y[j];
	for(register int i=0;i<zlen;i++)
		if(z[i]>9)
		{
			z[i+1]+=z[i]/10;
			z[i]%=10;
		}
	while(zlen>1 && !z.back())
	{
		z.pop_back();
		zlen--;
	}
	return z;
}
vector<int> chu(int x,vector<int> vv)
{
    bool flag=false;
    vector<int> ans;
    int t=0;
    int i;
    for (i=vv.size()-1; i>=0; i-- )
    {
        t=t*10+vv[i];
        if(flag)
        {
            ans.push_back(t/x);
        }
        else if(t/x>0)
        {
            flag=true;
            ans.push_back(t/x);
        }
        t=t%x;
    }
    return vector<int> (ans.rbegin(),ans.rend());
}
vector<int> add(vector<int>x,vector<int>y)//高精+高精
{
	int xlen=(int)x.size(),ylen=(int)y.size(),zlen=max(xlen,ylen)+1;
	vector<int>z(zlen);
	for(register int i=0;i<zlen;i++)
	{
		if(i<xlen)
			z[i]+=x[i];
		if(i<ylen)
			z[i]+=y[i];
		if(z[i]>9)
		{
			z[i+1]++;
			z[i]-=10;
		}
	}
	while(zlen>1 && !z.back())
	{
		zlen--;
		z.pop_back();
	}
	return z;
}
void prin(vector<int> x)
{
    int i;
    if(x.size() == 0)
    {
    	printf("0");
    }
    for (i=x.size()-1;i>=0;i--)
    {
        printf("%d",x[i]);
    }
    printf("\n");
}
std::vector<int> qup(std::vector<int> vv)
{
	std::vector<int> ans;
	ans.pb(1);
	std::vector<int> v;
	std::vector<int> f;
	f.pb(0);
	v.pb(2);
	while(vv.size() > 0 && *(--vv.end()) != 0)
	{
		if(vv[0] & 1)
			ans = mult(v,ans);
		vv = chu(2,vv);
		v = mult(v,v);
	}
	return ans;
}

char a[maxn];
vector<int> dg(int l,int r)
{
	vector<int> ans;
	
	if(l == r)
	{
		ans.pb(a[l] - '0');
		return ans;
	}
	ans.pb(0);
	std::vector<int> temp;
	for (int i = l; i <= r; i ++ )
	{
		
		if(a[i] == '(')
		{
			int s= 0 ;
			for(int j = i + 1; j <= r; j ++ )
			{
				if(a[j] == '(')
					s ++ ;
				if(a[j] == ')')
				{
					if(s == 0)
					{
						ans = add(ans,qup(dg(i + 1, j - 1)));
						i = j;
						break;
					}
					else
						s -- ;
				}
			}
			continue;
		}
		if(a[i] == '+')
		{
			ans = add(ans,dg(i + 1,r));
			break;
		}
		else
		{
			if(a[i + 1] != '(')
			{
				temp.pb(a[i] - '0');
				ans = add(ans,temp);
			}
			continue;
		}
	}
	return ans;
}

int main()
{
	scanf("%s",a + 1);
	int n = strlen(a + 1);
	vector<int> ans = dg(1,n);
	prin(ans);
}

E. Groundhog Chasing Death

题意

给出a,b,c,d,x,y,求  modulo 998244353的结果

题解

第一想法肯定是暴力:枚举 i 和 j ,然后算出每一个 gcd(xi , yj),然后乘起来。但是数据 i 和 j 的数据范围都在1e6,这样肯定会超时,i 和 j 的范围在1e9,这样肯定是要爆long long的。那么就要想想怎么优化。
因为这是一个乘性函数,那么肯定要想到分解质因子,所以先预处理 x 的质因子,并记录每个质因子出现的次数,这样 x就可以由每个质因子个数再 *i 得到。枚举 x中因子 m 出现的次数,可以发现当 yj 小的时候,gcd(x, yj) 主要是被 yj 的 m 个数约束,此时是一个等差数列;当 yj 大的时候,就是由 xi 约束,此时 yj 中 m 的个数是一个常数,暴力求解就好了。
由于质因子的幂很大,会爆long long,所以要用欧拉降幂,欧拉降幂是对mod-1取模(队友对mod取模debug好久

代码

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

typedef long long ll;
const ll mod = 998244353;
const ll md = 998244352;
vector<pair<ll,ll> > v;

void div(ll n)
{
	v.clear();
	ll k = sqrt(n);
	for (ll i = 2; i <= k; ++ i) {
		ll cnt = 0;
		while (n % i == 0) {
			n /= i;
			cnt ++;
		}
		v.push_back({i, cnt});
	}
	if (n != 1) {
		v.push_back({n, 1});
	}
}

ll phi(ll n)
{
    ll ans = n;
    ll k = sqrt(n);
    for (int i = 2; i <= k; ++i) {
        if (n % i == 0) {
            ans = ans / i * (i - 1);
            while (n % i == 0) n /= i;
        }
    }
    if (n > 1) {
        ans = ans / n * (n - 1);
    }
    return ans;
}

ll pow(ll a, ll b, ll p) {
	ll res = 1;
	while (b) {
		if (b & 1) res = (res * a) % p;
		a = a * a % p;
		b >>= 1;
	}
	return res%p;
}

int main()
{
	ll a, b, c, d, x, y;
	scanf("%lld%lld%lld%lld%lld%lld", &a, &b, &c, &d, &x, &y);
	a = max(1LL, a);
	c = max(1LL, c);
	--c;
	div(x);
	ll ans = 1;
	for (int i = 0; i < (int)v.size(); ++ i) {
		ll cnt = 0;
		while (y % v[i].first == 0) {
			y /= v[i].first;
			++ cnt;
		}
		ll sum = 0;
		if (cnt == 0) continue;
		for (ll j = a; j <= b; ++ j) {
			ll e = v[i].second * (ll)j;
			ll f = e/cnt;
			while (cnt * f < e) {
				++ f;
			}
			if (c >= f) {
				sum -= ((f * (f-1) / 2)% md * (cnt % md)) % md;
				sum = (sum % md + md) % md;
				sum -= (ll)(((c-f+1) % md) * (e % md)) % md;
				sum = (sum % md + md) % md;
			}
			else {
				sum -= ((c * (c+1) / 2) % md * (cnt % md)) % md;
				sum = (sum % md + md) % md;
			}
			sum = (sum % md + md) % md;
			if (d >= f) {
				sum += ((f * (f-1) / 2) % md) * (cnt % md) % md;
				sum = (sum % md + md) % md;
				sum += (ll)(((d-f+1) % md) * (e % md)) % md;
				sum = (sum % md + md) % md;
			}
			else {
				sum += (((d * (d+1) /2) % md) * (cnt) % md) % md;
				sum = (sum % md + md) % md;
			}
		}
		sum = (sum % md + md) % md;
		ans *= pow(v[i].first, sum, mod);
		ans = (ans % mod + mod) % mod;
	}
	ans = (ans % mod + mod) % mod;
	printf("%lld\n", ans);
	return 0;
}

F. Groundhog Looking Dowdy

题意

土拨鼠要和苹果约会,第 i 天它的第 j 件衣服的邋遢度为 aij ,它要在n天中选择m天和苹果约会,要求选出的m天衣服的邋遢度的最大值和最小值的差值最小。

题解

因为要最小化最大值和最小值的差值,所以用pair存每件衣服的邋遢度和第几天,然后对邋遢度从小到大排序。那么问题就转换成了求一个区间 [L,R] ,使得这个区间覆盖m个不同的天,并且R的邋遢度-L的邋遢度最小。这个问题就可以用尺取解决了。对于每一个L,求出最小的合法的R,每次更新差值最小值即可。

代码

#include<bits/stdc++.h>
#define st first
#define sd second
#define ll long long
#define pii pair<int,int>
using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 2e6+10;

pii pp[maxn];
int vis[maxn];

int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	int cnt = 0;
	for (int i = 1; i <= n; i ++ ){
		int p;
		scanf("%d",&p);
		for (int j = 1; j <= p; j ++ ){
			int x;
			scanf("%d",&x);
			pp[++cnt].st = x;
			pp[cnt].sd = i;
		}
	}
	sort(pp + 1, pp + 1 + cnt);
	int num = 0;
	int l = 1, r = 1;
	int minn = pp[1].st, maxx = 0;
	int ans = inf;
	while(1){
		while(r <= cnt && num < m){
			if(vis[pp[r].sd] == 0) num ++ ;
			vis[pp[r].sd] ++ ;
			maxx = pp[r].st;
			r ++ ;
		}
		if(num < m)
			break;
		ans = min(ans, maxx - minn);
		vis[pp[l].sd] -- ;
		if(vis[pp[l].sd] == 0) num -- ;
		minn = pp[l + 1].sd;
		l ++ ;
	}
	printf("%d\n",ans);
}

I. The Crime-solving Plan of Groundhog

题意

给出n个数(0<=a[i]<=9),由这n个数组成2个数,使得这两个数的乘积最小。

题解

自己造几个例子就可以看出,选择0之外最小的数和剩下的数字组成的没有前导0的数相乘结果最小。剩下的数字怎样最小呢,当然是找到最小的非零的数做第一位,后边接上所有的0,再按照有小到大的顺序放剩下的数字。因为n的范围在1e5,所以要用高精度乘法。

推导:

把当前的数字拆成4个数 a, b, c, d (a ≤ b ≤ c ≤ d) ,那么我们有两种决策:两位数×两位数,或者三位数×一
位数。
(10a + d) * (10b + c) = 100ab + 10ac + 10bd + cd 
(100b+10c+d) * a = 100ab + 10ac +ad<(10a + d) * (10b + c)
同理,可以证明留一个最小的正整数作为第一个数,剩下的所有数字排成最小的数作为第二个数时,答案取到最小值。

代码

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

int a[100100];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        for(int i=0;i<n;i++) cin>>a[i];
        sort(a,a+n);
        int k=0;
        while(k<n&&a[k]==0) k++;
        vector<int>q;
        q.push_back(a[k+1]);
        for(int i=0;i<k;i++) q.push_back(0);
        for(int i=k+2;i<n;i++) q.push_back(a[i]);
        vector<int>ans;
        for(int i=q.size()-1;i>=0;i--){
            int x=q[i]*a[k];
            ans.push_back(x);
        }
        for(int i=0;i<10;i++) ans.push_back(0);
        for(int i=0;i<ans.size();i++){
            if(ans[i]>=10){
                ans[i+1]+=ans[i]/10;
                ans[i]%=10;
            }
        }
        while(!ans.back()) ans.pop_back();
        for(int i=ans.size()-1;i>=0;i--) cout<<ans[i];
        cout<<endl;
    }
    return 0;
}

K. The Flee Plan of Groundhog

题意

土拨鼠在第1个宿舍,橙子在第n个宿舍。这n个宿舍间有n-1条路并且长度都为1,土拨鼠从第1个房间去第n个宿舍,速度为1m/s;橙子从第n个宿舍追赶土拨鼠,速度为2m/s。

题解

二分时间 t ,然后判断在 ts 内土拨鼠是否会被橙子追上。以橙子所在的寝室 n 为根建树,从 1 到 n 枚举所有土拨鼠能够到达的点,先找出t秒能走到哪个点,然后再找这个点能走到的离n最远的点,判断在走的过程中会不会被追上。

代码

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

const int maxn = 1e5+10;
vector<int> vv[maxn];
int vis[maxn];
int f[maxn];
int dep[maxn];

void dfs(int x,int fa,int de)
{
	f[x] = fa;
	dep[x] = de;
	for (int i =0 ; i< vv[x].size(); i ++ )
	{
		int v = vv[x][i];
		if(v == fa)
			continue;
		dfs(v,x,de + 1);
		vis[x] |= vis[v];
	}
}
int ans =0 ;
void dfs2(int x,int fa,int de)
{
	ans = max(ans,de);
	for (int i =0 ; i<vv[x].size(); i ++ )
	{
		int v = vv[x][i];
		if(v == fa)
			continue;
		dfs2(v,x,de + 1);
	}

}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for (int i = 1; i < n; i ++ )
	{
		int x,y;
		scanf("%d%d",&x,&y);
		vv[x].push_back(y);
		vv[y].push_back(x);
	}
	vis[1] = 1;
	dfs(n,0,1);
	int k = -1;
	int s = dep[1] - m;
	for (int i = 1; i <= n; i ++ ){
		if(vis[i] && dep[i] == s){
			k = i;
			break;
		}
	}
	int num = dep[k] - 1;
	dfs2(k,f[k],1);
	ans -- ;
	num += ans;
	int r = (num + 1) / 2;
	int l = 0;
	while(l < r){
		int mid = l + r>> 1;
		int x = min(2 * mid,num);
		int y = min(mid, ans);
		if(x - dep[k] + 1>= y) r = mid;
		else l = mid + 1;
	}
	printf("%d\n",l);
}