【简要题解】OI Online能力测试校内赛(Round One)T2~T4

T2.完美数

不说了吧。套路二分。

考场上 100 p t s 100pts 100pts

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
//二分显然
//不过本题可以从进制的角度考虑 
inline int read()
{
	int X=0; bool flag=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}
struct Data{
	int num,dat;
	friend bool operator <(Data x,Data y) {
		return x.num*x.dat>y.num*y.dat;
	} 
}b[N];
int n,a[N],c[N],cnt;
bool check(int mid) {
	for(int i=1;i<=cnt;i++) {
		int t=mid,tt=0;
		while(t>0) tt+=t/b[i].num,t/=b[i].num;
		if(tt<b[i].dat) return 0;
	}
	return 1;
}
signed main() {
    n=read();
    for(int i=1;i<=n;i++) {
    	a[i]=read();
    	for(int j=2;j<=sqrt(a[i]);j++) {
    		if(a[i]%j==0) {
    			if(!c[j]) b[++cnt].num=j,c[j]=cnt;
    			while(a[i]%j==0) b[c[j]].dat++,a[i]/=j;
			}
		}
		if(a[i]>1) {
			if(!c[a[i]]) b[++cnt].num=a[i],c[a[i]]=cnt;
			b[c[a[i]]].dat++;
		}
	}
    sort(b+1,b+1+cnt);
    int l=1,r=1e18,res=0;
    while(l<=r) {
    	int mid=(l+r)>>1;
    	if(check(mid)) r=mid-1,res=mid;
    	else l=mid+1;
	}
	printf("%lld",res);
}

T3.诗意狗

怎么说呢,考场上一眼就想到从叶节点开始考虑了。不过A的人中只有我一个人打了个类似于拓扑排序的队列。

由于把叶节点错判为 i n [ i ] = 1 in[i]=1 in[i]=1,导致最后向上传递时没有得到最优解,只有 85 p t s 85pts 85pts

还是挺可惜的,下次注意无根树的处理,最好转化成有根树,否则分不清父子关系。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
inline int read()
{
	int X=0; bool flag=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}
int n,m,res,in[N],vis[N];
int head[N*2],nxt[N*2],to[N*2],cnt;
queue<int> Q;
void add(int x,int y) {
	to[++cnt]=y,nxt[cnt]=head[x],head[x]=cnt;
}
signed main() {
    int T=read();
    while(T--) {
    	cnt=res=0;
    	n=read(),m=read();
    	for(int i=1;i<=n;i++) head[i]=in[i]=vis[i]=0;
    	for(int i=2,j;i<=n;i++) j=read(),add(i,j),in[j]++;
    	for(int i=1;i<=n;i++) if(!in[i]) vis[i]=2,Q.push(i);
    	//0:未访问
		//1:已入队,与儿子配对
		//2:已入队,与父亲配对 
		//也许是错解?反正又没有大样例
		//从叶节点开始考虑,与唯一的父节点配对。像这样尽量多地两两配对,剩下的每次配对花费就为1了 
    	while(Q.size()) {
    		int x=Q.front();Q.pop();
    		for(int i=head[x];i;i=nxt[i]) {
    			int y=to[i]; 
    			if(vis[x]==2) vis[y]=1;
    			if(vis[x]==1&&!vis[y]) vis[y]=2;
    			if(--in[y]==0) {
    				Q.push(y);
    				if(vis[y]==1) res++;//只有在父节点处配对成功才算 
				}
			}
		}
		if(res*2>=m) printf("%d\n",(m+1)/2);
		else printf("%d\n",res+m-res*2);
	}
}

T4.山海

也是看了几眼,想着 d p dp dp 瞎搞,一不小心就把状态看出来了。这次考试时间太短了,只有 2.5 h 2.5h 2.5h,导致没有认真调错,从 80 p t s 80pts 80pts掉到了 20 p t s 20pts 20pts

预处理是一个很基础的容斥。容斥可以用一句话解释:奇加偶减

容斥可以解决很多数论的问题。好家伙

#include<bits/stdc++.h>
using namespace std;
const int N=3005;
const int M=1e7+5;
const int mod=10007; 
inline int read()
{
	int X=0; bool flag=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}
int gcd(int x,int y) {
	if(y==0) return x;
	return gcd(y,x%y);
}
int n,m,k,a[N],dp[N][N],cnt;
int son[N][N];//预处理每个数的因数 
int pos[N][N];
int siz[N];
int rc[N];
int prime[N],num,sum;
//klnk 

//int checker(int x) {//[1,x]中与k互质的个数(怎么做到O(1)回答?) 
//    return (Tot*(x/k)%mod+sum[x%k])%mod;
//}
void dfs(int a,int b,int c,int d) {
	if(a>num) {
		sum+=d/c*b;//奇加偶减 
		return;
	}
	dfs(a+1,b,c,d);
	dfs(a+1,-b,c*prime[a],d);
}
signed main() {
//	freopen("data.in","r",stdin);
//	freopen("own.out","w",stdout);
    n=read(),m=read(),k=read();
    for(int i=1;i<=sqrt(k);i++) {
    	if(k%i==0) a[++cnt]=i;
    	if(k%i==0&&i*i!=k) a[++cnt]=k/i;
	}
	sort(a+1,a+1+cnt);
	for(int i=1;i<=cnt;i++) {
		for(int j=1;j<=i;j++)
	        if(a[i]%a[j]==0) {
	        	siz[i]++;
	        	son[i][siz[i]]=j;
	        	int t=lower_bound(a+1,a+1+i,a[i]/a[j])-a;
	        	pos[i][siz[i]]=t;
			}
		if(siz[i]==2) {
			prime[++num]=a[i];
		}
	}
	for(int i=1;i<=cnt;i++) {
		sum=0;
		dfs(1,1,1,m/a[i]);
		rc[i]=sum%mod;
	}
	dp[0][1]=1;
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=cnt;j++) {
			for(int k=1;k<=siz[j];k++) {
				dp[i][j]=(dp[i][j]+dp[i-1][pos[j][k]]*rc[son[j][k]]%mod)%mod;
			}
		}
	}
	printf("%d",dp[n][cnt]);
}

说说收获吧。收获是:我变强了,教训是:挂分变多了,想到正解但没时间打或者犯一些低级错误,总是在细节的地方丢分。

还是挺开心的。建议多打这种有教育意义的比赛。

所以这就是平时考试这么毒瘤和没有人性化的大样例的原因???毒瘤的难以置信。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值