南昌邀请赛 赛后总结与补题

以下所有代码为自己xjb写的,不确定对不对,如果有重现赛,可以交一交

后续:7月23号重现赛,都交了一下,发现都可以过,mdzz

 

本次邀请赛,说明我们队还是离邀请赛金或者区域赛稳银还是有很大的距离的,

一方面,是暴露出的种种的不足;另一方面,可能需要反思的是平时的学习模式的问题

毕竟在ACM的道路上,还是有种种不利因素,阻止着变强啊

 

热身赛

AD水题,B是大模拟没写出来,

C是一个类似康托展开的魔改,

在允许字母可重的情况下,对字符串s(|s|<=16)全排列之后去重,询问第k大的排列

一开始敲了一发康托展开的板子,打表输出了一些奇奇怪怪不按字典序出牌的东西

后来被归神指出重复排列会被当成两个,卡了会之后想到了枚举这一位填什么字母,增序枚举,

所在的rank区间[L,R],如果[L,R]包含k就填这一位,否则就加上这段rank,再往大里填

然而写了1个小时,回宾馆调了调调过样例了也不知道对不对

当然,写一半的时候,主办方说BC数据出锅了,不然那么多金爷怎么可能做不出来

#include
using namespace std;
typedef long long ll;
bool flag;
char ans[20],s[20];
int now[26],len;
ll A[20],p,tmp,rk;
int main()
{
	A[0]=1;
	for(int i=1;i<=16;++i)
	A[i]=A[i-1]*i;
	while(~scanf("%s%lld",s,&p))
	{
		if(s[0]=='#'&&!p)break;
		len=strlen(s);
		memset(now,0,sizeof now);
		rk=1;
		for(int i=0;i

 

正赛

槽,正赛有三份英文题面一份中文题面是什么鬼

L题,直接输出5个数,2min1Y,我也想0min1Y啊,然而找到这个题敲完就2min了

K题,a[i]的前缀和,排个增序,小的对应乘小的,排序不等式和最大,输出这个和,归神单开,7min1y

F题,1e5个数,1e5个询问,两种操作,一个单点改数,另一个询问[L,R],

其每个L<=l<=r<=R的子区间(l,r)的贡献定义为这个子区间所有值的异或,求[L,R]内所有子区间的贡献的异或

涛神率先提出的思路,统计每个值被计了多少次,是(i-l+1)*(r-i+1)次,然后分l和r是否同奇偶讨论,

如果同奇偶,再分i是否和l、r同奇偶讨论,奇数位置开一棵线段树,偶数位置开一棵线段树

归神的线段树写了点小bug,我跟着调出来了,39min1y,

想了想BIT也可以维护区间异或,觉着要是敲着更熟练的话,时间可以更短,赛后试了下的确如此

看了一下榜,J题和G题各有几个队过,我开了J,归神看了G

J是一个裸的字典树,1e5个串,串总长小于2e5,每个小写字母对应一个hash值,

整个串的hash值是所有字母hash值的乘积%m,对于每个串问有多少前缀满足在模意义下,hash值比整个串小,

我约20分钟敲了个板子然而结果不对,归神这时候G有了思路先让他敲G,我只好打印代码debug,

后来发现,先是数组重名了,然后自己令rt=1结果开节点还在tot=0的情况下++tot,写好这里之后90min1y了,

的确是因为自己板子的问题,没能更快的1y,要整理一个好的字典树板子①

G题是一个贪心叭,1e5个人,每个人有三个权值ai,bi,ci,

题目保证ai两两不同,bi,ci同理,只要有一个权值比另一个人的对应权值大,这个人就可以赢另一个,

要举办n-1场比赛,主办方每次可以指定两个未淘汰选手,指定用哪个权值进行比较,

最后1e5个询问,询问每个人是否能成为冠军

首先,三个权值分别最大的人ABC是能成为冠军的,也因此更新了他们在别的领域的最小值,

如果D能战胜这些最小值的一个,就能让那个人先把n-2个人扫了之后,再最后一轮输给D,

所以就是不断更新最小值至不能再更新

归神提了一个按值更新的做法,RE了一发又WA了一发,这个时候我们想听思路,我问涛神涛神也没听懂,

涛神后来懂了帮归神debug,说两个人懂了就没必要再讲了,我就去新开题了,

我在这一个多小时是挂机的,想跟榜但是榜过别的题的人寥寥无几只有两三个,

然而归神心态有点崩,又分别WA和RE了一发,177min5y

看了一下榜,5题罚时404rank81,已经有八九十个队伍是五题及以上了,

且如果少一发罚时的话,能前进约10名左右,诶,又是一个罚时局

后两个小时就很无力了,一方面是有中餐之后代码输出力--(麦当劳中餐好评呀,感觉中餐投入得是其它赛区的两倍)

另一方面也是后期题我们知识重合的部分比较少,在配合方面,还有很多不足

我开了H题,涛神开了B题,期间也看过A题、D题和E题,

A题想过用费用流,但不会处理多次通过这条边只记录一次流量,赛后才知道竟然是斯坦纳树裸题……

看完几个题之后,感觉可能能做的也就B和H

涛神看了一会B题发现不会做,我跟着看了一会,提出如果能求\sum_{i=1}^{n}i^{k}就好了,然而仨人都不会求,

而且高斯消元的复杂度O(n^3)在这里解n=1e3的式子很拙计,更何况大家都只是天天口胡并没有套过板子

赛后才知道是裸的拉格朗日差值,而且我收藏夹里有拉格朗日差值,例题就是求这个式子,槽

我看了H题,发现前半部分是裸的FWT抄了一发板子,然而后半部分被我理解错了题意(背锅)

神奇的是,我在做这个题的时候对面电子科大的dove队9发破I之后也在搞这个题,

而且进度近乎和我一致,然而他们最后1h也没能过这个题,估计也是读错题了

是对FWT之后的序列维护两种操作,一种是[l,r]内暴力开根,另一种是询问位置x的值,

我当时以为开根完之后数变小会跑到前面去,赛后看知乎也有中学生和我理解的一样,用的splay,槽

然后我就和归神在那里敲权值线段树,每次暴力开[l,r]区间然后忽视已经值是1的部分,

我自己权值线段树敲被理解错的这题估计也需要2h,归神也坦言说自己可能也需要2h敲,

一方面是第五题导致时间不太够,另一方面也的确是大家的后期输出力可能还有待提升

然而开根之后的数的位置是不变的,于是就变成了二分位置之后一个离散化+线段树区间覆盖的板子题

另一方面,虽然我和归神都能手敲区间覆盖,但感觉这种板子还是要有的,毕竟赛场争分夺秒,不容出错

现在觉得也是最后一个小时大家都不大稳叭,

我那个时候固执地觉得是位置移动的,涛神也直接说自己挂机了,

就感觉仨人的状态都有下降吧,如果能完整地手跑一下样例,应该是能区别出这种情况的

赛后去请教山东理工大学过这个题的队伍解法之后,感觉不读错题的情况下,应该是可以1h调出来的

不过赛后也证明,我们的罚时自第五题之后连交第六题AC也不能金了,所以可能没什么好遗憾的,菜就完事了

5题银到铁,滚榜的时候看那个罚时一会1分钟2分钟的往前减一会掉七八分钟当时觉得很有可能是铜,

结果,rk81最后掉到rk85,算是捡了个银牌倒数叭,诶如果能过第六题,哪有这么多需要担忧的

一方面说明大家的水平可能差不多,另一方面也说明,在做中期题这方面,我们还是不如其他学校

喔,顺便一提,F题和J题好像是数据出锅了,被某些队伍F暴力/J两个map卡过去了

赛后逛知乎发现,I题是一个优雅的暴力的思维题,只是因为没人开;

C题欧拉降幂+dp,可补,但的确也是因为榜上过题人数少,没开

 

从个人方面分析一下自己可能存在的不足吧,有则改之,无则加勉

①思维题训练量不够

在这方面,CF还是利于培养思维,10多场和100多场的区别,高下立判

②赛后不补题的坏习惯

这个就不用多说了,蒟蒻ACMER的通病

③口胡题解之后不注重代码实现

AC is OK,其他都是0

④互膜的不良风气

可能这是高中OIER圈也存在的风气吧,只有自己内心深处,才知道自己多菜吧

⑤过题之后水群炫耀

多过题,少水群,少吹比,很多知识,只有自己不会

⑥刷水题寻找满足感

还是要走出自己的comfort zone叭,Claris也曾说过,只有做自己不会的题才会有提升

⑦学习新知识时死抠原理

这也是自己上学期只学知识不做题的弊病叭,更重要的是应用

⑧学知识时找偏难怪得学

有些知识学了之后八百年也不一定考一次,考的时候可能都不记得自己学过了

区域赛后期题也往往不会出裸题,邀请赛这种是特例 

⑨因为知识名字比较怪望而生畏

犹记得自己嚷嚷着学启发式合并到现在还没有学,拉格朗日插值至今躺在收藏夹里或许也是这个原因吧……

少从队友身上找理由,多从自己身上找不足,你是主代码手呀!

A题

#include
using namespace std;
/*
 *  Steiner Tree:求,使得指定K个点连通的生成树的最小总权值
 *  st[i] 表示顶点i的标记值,如果i是指定集合内第m(0<=m que;
mapmp;
string rd1,rd2;
void add(int u,int v,int w)
{
	V[++cnt]=v;
	W[cnt]=w;
	nex[cnt]=head[u];
	head[u]=cnt;
}
void initSteinerTree()
{
	mp.clear();
	num=cost=0;
	cnt=0;
	memset(head,0,sizeof head);
	memset(dp,-1,sizeof dp);
	memset(ans,-1,sizeof ans);
	memset(st,0,sizeof st);
    for(int i=1;i<=n;i++)
    memset(vis[i],0,sizeof vis[i]);
    //对每个st[i]赋值 对endSt赋值 
}
void input()
{
	for(int i=1;i<=n;++i)
	{
		cin>>rd1;
		mp[rd1]=i;
	}
	for(int i=1;i<=m;++i)
	{
		cin>>rd1>>rd2>>cost;
		u=mp[rd1],v=mp[rd2];
		add(u,v,cost);
		add(v,u,cost);
	}
	for(int i=0;i<4;++i)
	{
		cin>>rd1>>rd2;
		u=mp[rd1];v=mp[rd2];
		st[u]|=1<x || a==-1)? x : a;
}
void SPFA(int state)
{
    while(!que.empty())
	{
        int u=que.front();
        que.pop();
        vis[u][state]=false;
        for(int i=head[u];i;i=nex[i])
		{
            int v=V[i];//以v为根 不管v不是所选集合节点还是是 
            if(dp[v][st[v]|state]==-1 || dp[v][st[v]|state]>dp[u][state]+W[i])
			{
        	    dp[v][st[v]|state]=dp[u][state]+W[i];
    	        if(st[v]|state!=state || vis[v][state])continue; //只用当前state更新所有根节点i的dp[i][state]的最小值 
        	    vis[v][state]=true;
       	 	    que.push(v);
            }
        }
    }
}
void steinerTree()
{
    for(int j=1;j>i&1)res^=(1<>i&1)res^=(1<<(i-4));
	return res==0;
}
void solve()
{
	for(int j=1;j>n>>m)
	{
		initSteinerTree();
		input();
		steinerTree();
		solve();
	}
	return 0;
}

B题

#include
using namespace std;
typedef long long ll;
const int mod=9999991;
const int N=1e3+10;
int t,n,m,l,r;
int a[N],sum[N],pre[N],suf[N];
int fac[N],finv[N];
int modpow(int x,int n,int p)
{
	int res=1;
	for(;n;x=1ll*x*x%p,n>>=1)
	if(n&1)res=1ll*res*x%p;
	return res;
} 
int cal(int *f,int mx,int n)//已知f[0]到f[mx] 求f[n] 
{   
	if(n<=mx)return f[n];
    int ans=0;
	pre[0]=suf[mx]=1;
	for(int i=1;i<=mx;++i)
	pre[i]=1ll*pre[i-1]*(n-i+1)%mod;
	for(int i=mx;i>=1;--i)
	suf[i-1]=1ll*suf[i]*(n-i)%mod;
	for(int i=0;i<=mx;++i)
	{
		int sg=(mx-i)&1?-1:1;
		ans=ans+1ll*sg*pre[i]%mod*suf[i]%mod*finv[i]%mod*finv[mx-i]%mod*f[i]%mod;
		if(ans>=mod)ans-=mod;
		if(ans<0)ans+=mod;
	}
    return ans;
}
void init()
{
	fac[0]=1;
	for(int i=1;i=1;--i)
	finv[i-1]=1ll*finv[i]*i%mod;
}
int main()
{
    init();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<=n;i++)
        scanf("%d",&a[i]);
        a[n+1]=cal(a,n,n+1);//插出f(n+1)
        sum[0]=a[0];//非常关键 别忘了 
        for(int i=1;i<=n+1;i++)
        sum[i]=(sum[i-1]+a[i])%mod;
        while(m--)
		{
            scanf("%d%d",&l,&r);
            int cnt=cal(sum,n+1,r)-cal(sum,n+1,l-1);
            if(cnt<0)cnt+=mod;
            printf("%d\n",cnt);
        }
    }
    return 0;
}

C题

#include
#include
#include
#include
#include 
using namespace std;
typedef long long ll;
const int p=1e5+3;
const int maxn=1e5+10; 
const int N=1e5+10;
const int M=5e3+5;
bool ok[maxn];
int prime[maxn],phi[maxn],cnt;
int a,b,n;
int u[N],v[N];
int dp[2][M][2]; 
void sieve()
{ 
        phi[1]=1;
	for(ll i=2;i=maxn)break;
			ok[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];//prime[j]是i的因子 prime[j]的素因子项包含在i的素因子项里
				break; 
			}
			else phi[i*prime[j]]=phi[i]*(prime[j]-1);//prime[j]与i互质 phi[i*prime[j]=phi[i]*phi[prime[j]]
		}
	}
}
int modpow(int x,int n,int mod)
{
	int ans=1;
	for(;n;n/=2,x=1ll*x*x%mod)
	if(n&1)ans=1ll*ans*x%mod;
	return ans;
}
int f(int num,int mod)
{
	if(mod==1)return 0;
	if(num==1)return b%mod;
	return modpow(b,f(num-1,phi[mod])+phi[mod],mod);
}
int main()
{
	sieve();
	while(~scanf("%d%d",&a,&b))
	{
		scanf("%d",&n);
		for(int i=1;i<=n;++i)
		{
			scanf("%d",&u[i]);
			u[i]=modpow(a,f(u[i],phi[p])+phi[p],p);
		}
		for(int i=1;i<=n;++i)
		{
			scanf("%d",&v[i]);
			v[i]=modpow(a,f(v[i],phi[p])+phi[p],p);
		}
		memset(dp,0,sizeof dp);
		u[0]=v[0]=p+1;//保证和第二项不同 
		for(int k=0;k<=n;++k)
		{
			int i=k&1;
			for(int j=0;j<=n;++j)
			{
				dp[i][j][0]=0;
				dp[i][j][1]=0;
			}
			for(int j=0;j<=n;++j)
			{
				if(k>0)
				{
					dp[i][j][0]=max(dp[i][j][0],dp[i^1][j][0]+u[k]*(u[k]==u[k-1]));
					dp[i][j][0]=max(dp[i][j][0],dp[i^1][j][1]+u[k]*(u[k]==v[j]));
				}
				if(j>0)
				{
					dp[i][j][1]=max(dp[i][j][1],dp[i][j-1][0]+v[j]*(v[j]==u[k]));
					dp[i][j][1]=max(dp[i][j][1],dp[i][j-1][1]+v[j]*(v[j]==v[j-1]));
				}
			}
		}
		printf("%d\n",max(dp[n&1][n][0],dp[n&1][n][1])); 
	}
	return 0;
}

F题

#include
using namespace std;
const int maxn=1e5+10;
int tr[2][maxn],a[maxn];
int t,n,m;
int op,l,r;
void add(int *a,int x,int v)
{
	for(int i=x;i<=n;i+=i&-i)
	a[i]^=v; 
}
int Xor(int *a,int x)
{
	int ans=0;
	for(int i=x;i>0;i-=i&-i)
	ans^=a[i];
	return ans;
}
int main()
{
	scanf("%d",&t);
	for(int cas=1;cas<=t;++cas)
	{
		memset(tr,0,sizeof tr);
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;++i)
		{
			scanf("%d",&a[i]);
			if(i&1)add(tr[0],i,a[i]);
			else add(tr[1],i,a[i]);
		}
		printf("Case #%d:\n",cas);
		for(int i=1;i<=m;++i)
		{
			scanf("%d%d%d",&op,&l,&r);
			if(!op)
			{
				if(l&1)add(tr[0],l,a[l]),add(tr[0],l,r);
				else add(tr[1],l,a[l]),add(tr[1],l,r);
				a[l]=r;
				continue;
			}
			if((r-l)&1){puts("0");continue;}
			else
			{
				if(l&1)printf("%d\n",Xor(tr[0],r)^Xor(tr[0],l-1));
				else printf("%d\n",Xor(tr[1],r)^Xor(tr[1],l-1));
			}
		}
	}
	return 0;
}

G题

#include
using namespace std;
const int maxn=1e5+10;
const int INF=0x3f3f3f3f;
struct node
{
	int v,id;
	node(){}
	node(int vv,int i):v(vv),id(i){
	}
}tmp;
bool operator<(node a,node b)
{
	return a.vp[3];
int mn[3],a[3][maxn];
int n,q,v;
bool ok[maxn],flag;
int main()
{
	for(int i=0;i<3;++i)
	mn[i]=INF;
	scanf("%d%d",&n,&q);
	for(int j=0;j<3;++j)
	{
		for(int i=1;i<=n;++i)
		{
			scanf("%d",&a[j][i]);
			p[j].push(node(a[j][i],i));
		}
	}
	for(int i=0;i<3;++i)
	{
		tmp=p[i].top();p[i].pop();
		ok[tmp.id]=1;
		mn[i]=min(mn[i],a[i][tmp.id]);
		mn[(i+1)%3]=min(mn[(i+1)%3],a[(i+1)%3][tmp.id]);
		mn[(i+2)%3]=min(mn[(i+2)%3],a[(i+2)%3][tmp.id]);
	}
	flag=1; 
	while(flag)
	{
		flag=0;
		for(int i=0;i<3;++i)
		{
			while(!p[i].empty()&&p[i].top().v>mn[i])
			{
				tmp=p[i].top();p[i].pop();
				if(ok[tmp.id])continue;
				ok[tmp.id]=1;flag=1;//代表本次更新有人被取出 
				mn[(i+1)%3]=min(mn[(i+1)%3],a[(i+1)%3][tmp.id]);
				mn[(i+2)%3]=min(mn[(i+2)%3],a[(i+2)%3][tmp.id]);
			}
		}
	}
	for(int i=1;i<=q;++i)
	{
		scanf("%d",&v);
		puts(ok[v]?"YES":"NO");
	}
	return 0;
} 

H题

#include
using namespace std;
typedef long long ll;
const int maxn=3e5+5;
const int maxm=1e5+5;
int n,m;
ll x[maxn],y[maxn];
ll dat[maxn*5],cov[maxn*5];
ll a[maxn],b[maxn],N,mx,v;
ll num[maxn],cnt,tot;
ll in[maxn],cal;
ll sum[maxn];
struct node
{
	ll x,y,id;
	ll tox,toy;
	node(){}
	node(ll xx,ll yy,ll p):x(xx),y(yy),id(p){}
}e[maxm];
void pushup(int p)
{
	dat[p]=dat[p<<1]+dat[p<<1|1];
}
void pushdown(int p,int l,int r)
{
	if(cov[p])
	{
		int mid=l+r>>1;
		cov[p<<1]+=cov[p];
		dat[p<<1]+=cov[p]*(mid-l+1);
		cov[p<<1|1]+=cov[p];
		dat[p<<1|1]+=cov[p]*(r-mid);
		cov[p]=0;
	}
}
void update(int p,int l,int r,int ql,int qr)
{
	if(ql<=l&&r<=qr)
	{
		dat[p]+=r-l+1;
		cov[p]++;
		return;
	}
	pushdown(p,l,r);
	int mid=l+r>>1;
	if(ql<=mid)update(p<<1,l,mid,ql,qr);
	if(qr>mid)update(p<<1|1,mid+1,r,ql,qr);
	pushup(p);
}
int ask(int p,int l,int r,int pos)
{
	if(l==r)return dat[p];
	pushdown(p,l,r);
	int mid=l+r>>1;
	if(pos<=mid)return ask(p<<1,l,mid,pos);
	else return ask(p<<1|1,mid+1,r,pos);
}
void FWT_or(ll *a,int op)
{
	for(int i=1;i6){puts("1");continue;}
			for(int j=1;j<=res;++j)
			c=sqrt(c);
			printf("%lld\n",c);
		}
		else update(1,1,tot,e[i].tox,e[i].toy);
	}
	return 0;
} 
/*
5
6 2 6 4 5
1 7 9 10 3
5
17 21
0 8
8 13
0 8 
0 20
*/ 

 

你可能感兴趣的:(赛后总结与补题)