ICPC济南区域赛

萌新第一次上大学打区域赛,以为比较水,谁知道直接被打自闭了....幸好手快,在第30分钟的时候过了签到题,这才狗到了铜牌末尾,当初听到的难道不是四题铜,5,6题银吗,7题金吗?...怎么这一题铜,二题银,三题金是怎么回事?....着实自闭了...
慢慢补题ing,下周还有ccpc的威海站,加油!!大一 一定要拿银,这样愿望就提前实现了...
就是这pta平台是真的不适应,补题还只能买考试卷和时空机...坑爹的玩意...
先来看看签到题,(感谢让我拿桐的一题)

K Search For Mafuyu

题目大意:给你一棵树,你的伙伴藏在了除一号节点中的某个节点,问你怎么走,才能使得你找到他的期望时间最小。求最小的期望时间?每走一个边的代价都是1.
首先可以得出答案为\(\frac{1}{n-1}\sum t_i\)\(t_i\)表示到达某个点的时间。
首先第一个想法就是如果你走到某个点的时候,你一定是要将当前以该点为根的子树走完才从该点离开,即你不可能走到这个点,不往下走,往上走,然后在某个时刻又来到这个点再往下走...这只能看显然法了....接下来考虑对一个点x,怎么走他的儿子y,假设这里有三个儿子y1,y2,y3,先考虑y1与y2的顺序,显然y1,y2,y3,与y2,y1,y3的顺序对于y3的答案统计是没有影响的。接下来就考虑y1与y2的内部点的答案是否相同,设\(size_i\)为子树大小,令\(d_i\)为从来到此节点时时间为0,最后走完所有该子树节点造成的贡献。对于y1,y2的顺序而言,答案为
\(d_{y1}+size_{y1}*size_{y2}+d_{y2}\),考虑y2,y1,发现答案任然是这样的。即到某个点后儿子的顺序之间是等价的,美滋滋,随便走就能过。
代码就不贴了,过于简单。

C Optimal Strategy

这是也是一道“签到题”,根据出题人的定义,过的队伍>=100就是签到题,可当时我们队伍对于这个有180多个队伍都过了的题竟一点想法都没有...还是自己太菜了。
题目大意:这里有\(n\)个石子,每个石子上都有一个数值,两个人轮流取石头,两个人都希望最后自己的石头的数值足够大,问在两人都足够聪明的情况下,一共会有多少种不同的过程?
\(p_i\)为某个比赛过程中两人轮流取得石头的位置信息,两个过程不同则由某个\(p_i\)不同。
这个题刚开始的想法就是两个人都希望自己获得的石头的价值后足够大,那直接每次直接取最大值不就行了?可为什么还会有不同的过程。首先可能数值相同的石头有很多,我们选他们中的任意一个都行,但却要算作是不同的方案。其次还有不一定要拿最大的,拿其他的也能保证我最后拿到的和是最大的。
这个时候就可以分析每个人取石头时的最优策略,首先若一堆石头个数是奇数的话,先手可以拿走更多的该数值石子。根据这个规则,我们可以考虑数值最大的石子的数量记为cnt,若cnt为奇数,则肯定要拿这个石子,若为偶数,则无论先手后手都可以拿走相同数量的该数值的石子。但若一旦有人在偶数的情况下拿走该数值的一个石头,那么剩下那个人就面临最大数值为奇数的局面,则他一定会拿该数值的石头。也就是说我们可以将该cnt个石头进行绑定,分成cnt/2对,(仔细想想,因为在偶数的情况下,肯定是成对拿的,奇数的话,单省一个一个的)。考虑当前数值为i的石头,什么时候拿,我们将其分成cnt/2后,单独剩下的就不用考虑顺序了,因为一定会一开始就拿走。这个时候可以考虑DP的思想,想f[i-1]已经统计过方案数了,考虑,cnt/2对 我们可以任意插入到之前的序列中(操作序 列)。这样的话,\(f[i]=f[i-1]*C_{sum[i-1]+cnt/2}^{cnt/2}*(cnt!)\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+10,P=998244353;
int n,cnt[N];

inline ll power(ll x,ll y)
{
    ll ans=1;
    while(y)
    {
        if(y&1) ans=ans*x%P;
        x=x*x%P;
        y>>=1;
    }
    return ans%P;
}

inline ll C(int n,int m) {return jc[n]*jc_inv[m]%P*jc_inv[n-m]%P;}

int main()
{
    freopen("1.in","r",stdin);
    scanf("%d",&a[i]);
    for(int i=1;i<=n;++i) 
    {
        int x;scanf("%d",&x);
        cnt[x]++;
    }
    for(int i=1;i<=n;++i) cnt[i]+=cnt[i-1];
    jc[0]=1;jc_inv[0]=1;
    for(int i=1;i<=n;++i) jc[i]=jc[i-1]*i%P;
    jc_inv[n]=power(jc[n],P-2);
    for(int i=n-1;i>=1;--i) jc_inv[i]=jc_inv[i+1]*(i+1)%P; 
    ll ans=1;
    for(int i=1;i<=n;++i)
    {
        int t=cnt[i]-cnt[i-1];
        if(!t) continue;
        ans=(ans*jc[t]%P*C(cnt[i-1]+t/2,t/2))%P;
    }
    printf("%lld",ans); 
    return 0;
}

J Determinant

我承认是我没看题....
题目大意:给定一个行列式,给出该行列式的值得绝对值,问该行列式最后的值是正还是负?
显然不是让你直接求行列式然后判正负,因为随随便便就爆了。数太大怎么办?要么取对数,要么取模,对啊,取模,绝对值取模,最后求行列式的时候取模,看最后值是否相等就行。...这种小技巧还是害....

D Arithmetic Sequence

这个题以为是思维题,考试的时候花了大部分时间希望能推出点性质,到最后,还是一无所获...害
题目大意:给定你一个序列,你可以进行若干次操作,使得某个数值加1或减1,最后使得整个序列为等差序列,问最少的操作次数?
看到等差序列,我的第一想法就是先找到差分序列,然后将原操作对应到差分序列上,看看有没有什么性质,可看了很久都没有看出来...路走窄了....
既然差分序列上找不到性质,那我们就返回到原序列上,既然直接求走不通,我们可以考虑固定一个条件,考虑固定公差d,设第一项是b+d,则\(ans=\sum_{i=1}^{n}|d*i+b-a_i|\),我们想让这个值最小,在每一项中\(d*i-a_i\)都是确定的,我们大可以令\(c_i=a_i-d*i\)这样的话最后答案就是\(ans=\sum_{i=1}^{n}|b-c_i|\),而关于这个问题是一个经典的货仓选址的问题,当b取中位数时最优,而假如给定了d,那我们可以用nth_element来在O(n)的复杂度内解决。至于d怎么选最优,这个真的去推的话,真的很难,考场上就根据自己的经验,打表,找规律。你完全可以根据样例打个表,然后就惊奇的发现答案随着d的增大是单谷函数,三分d就行。

#include<bits/stdc++.h>
#define ll __int128_t
using namespace std;
const int N=2e5+10;
int n;
ll a[N],c[N];

inline ll read()
{
	ll x=0,ff=1;
	char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return ff*x;
}

inline void put(ll x)
{
	if(x<10) putchar('0'+x);
	else 
    {
        put(x/10);
        putchar('0'+x%10);
    }
}

inline ll abs(ll x)
{
	if(x>0) return x;
	return -x;
}

inline ll solve(ll d)
{
	for(int i=1;i<=n;++i) c[i]=a[i]-d*i;
	int k=ceil(1.0*n/2);
	nth_element(c+1,c+k,c+n+1);
	ll ans=0;
	for(int i=1;i<=n;++i) ans+=abs(c[i]-c[k]);
	return ans;
}

int main()
{
//	freopen("1.in","r",stdin);
	scanf("%d",&n);
	for(int i=1;i<=n;++i) a[i]=read();
	if(n==1||n==2) {printf("0\n");return 0;}
	ll l=-2e13/(n-1),r=2e13/(n-1);
	while(l+3<r)
	{
		ll lmid=(l+r)/2;
		ll rmid=(lmid+r)/2;
		if(solve(lmid)<solve(rmid)) r=rmid;
		else l=lmid;
	}
	ll ans=3e18;
	for(ll i=l;i<=r;++i) ans=min(ans,solve(i));
	put(ans);
	printf("\n");
	return 0; 
} 

E Insidemen

这也算是比较好的思维题了,但由于比赛时签到题就卡死了,这个题当时也就简单的看了看题意就略过了...(我太菜了...)。
题目大意:现在有n个人,围成一圈,还有m条边,边都是在圈内部连接,这样导致某些边会有交,若有两条边他们的顶点分别是i,j,p,q,且着两条边相交,则他们造成的贡献是(i+j)*(P+q).但这n个人中有两个是偷懒者,偷懒者不产生贡献,即四个人都不是偷懒者的时候才能产生贡献,问最理想的情况下,总的贡献和最大是多少?(你不知道偷懒者是谁)?
n是1000,m是100000。

原文地址:https://www.cnblogs.com/gcfer/p/15557079.html