D - Square Permutation
其實比較簡單,但是比賽時候腦子不轉了,竟然在嘗試枚舉全排列,然後算了一下複雜度直接不會做了。
正解應該是枚舉完全平方數,底數枚舉到 \(sqrt(10^{14})\) 即可,因爲 n 最大爲 13。
然後統計一下這個完全平方數各個數字出現了多少個,和讀入的比較一下是否相等即可。
注意這個完全平方數不能超過 n 位,且不足 n 位時前面要補 0。
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<iomanip>
#include<ctime>
#include<stack>
using namespace std;
int n,ans,a[10],dig[10];
string s;
int main()
{
ios::sync_with_stdio(false);
cin>>n>>s;
for(int i=1;i<=n;i++) a[s[i-1]-'0']++;
for(int i=0;i<10000000;i++){
long long x=1ll*i*i;
memset(dig,0,sizeof(dig));
for(int i=1;i<=n;i++) dig[x%10]++,x/=10;
if(x) continue;
for(int i=0;i<=9;i++){
if(dig[i]!=a[i]){
ans--;
break;
}
}
ans++;
}
cout<<ans;
return 0;
}
F - Beautiful Path
這種算法其實以前學過,叫做 01分數規劃,即分子和分母都是一些數字的和,要求最大/小的這個分數值。
好久沒做的我賽場上以爲是個dp,寫了好久最後一直wa,賽後發現他不滿足最優子結構(到達u節點的分數最大時並不一定是最優解,因爲可能分子和分母都很大,導致後面一些價值高的邊“貶值”了)。
正解是二分這個最大值 X,然後判斷 \(\max \{ \sum (a_i-Xb_i) \} > 0\) 是否成立。
這種形式是可以用dp來求的。
日語題解裏好像提到了一種可以優化到O(n)的Dinkelbach 算法,沒看懂。
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<iomanip>
#include<ctime>
#include<stack>
using namespace std;
const double eps=1e-10;
const int maxn=200005;
int n,m,cnt,p[maxn],vis[maxn];
double dp[maxn];
struct node{
int v,next;
double val,w;
}e[maxn];
void insert(int u,int v,double val,double w){
cnt++;
e[cnt].v=v;
e[cnt].val=val;
e[cnt].w=w;
e[cnt].next=p[u];
p[u]=cnt;
}
bool check(double x){
memset(dp,0,sizeof(dp));
memset(vis,0,sizeof(vis));
vis[1]=1;
for(int u=1;u<=n;u++){
if(!vis[u]) continue;
for(int i=p[u];i!=-1;i=e[i].next){
int v=e[i].v;
if(vis[v]) dp[v]=max(dp[v],dp[u]+e[i].val-e[i].w*x);
else dp[v]=dp[u]+e[i].val-e[i].w*x;
vis[v]=1;
}
}
if(dp[n]>0) return 1;
return 0;
}
int main()
{
ios::sync_with_stdio(false);
memset(p,-1,sizeof(p));
memset(dp,-1,sizeof(dp));
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;
double w,val;
cin>>u>>v>>val>>w;
insert(u,v,val,w);
}
double l=0,r=10000;
while(abs(r-l)>eps){
double mid=(l+r)/2;
if(check(mid)) l=mid;
else r=mid;
}
cout<<fixed<<setprecision(10)<<l;
return 0;
}
比賽總結
忙了一天之後打比賽,累得甚至趴着桌子上睡了一會,崩掉大概是我能理解的。
ABC過的速度還可以,但是D題卡住了,於是先去做E,然後F題又假了。
比賽前還是應該調一下狀態,注意休息。