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]);
}
说说收获吧。收获是:我变强了,教训是:挂分变多了,想到正解但没时间打或者犯一些低级错误,总是在细节的地方丢分。
还是挺开心的。建议多打这种有教育意义的比赛。
所以这就是平时考试这么毒瘤和没有人性化的大样例的原因???毒瘤的难以置信。。。