新生賽 10
今天沒有學什麼演算法,主要是做了做往年的新生賽,雖然說估計應該最高只有一兩個綠題的水平,基本上是黃題,但我的水平可以保證不能穩切綠題,黃題十有八九吃罰時。: (
A
貪心,一開始還煞有介事地開了個優先佇列,給陣列排了個序。
事實上優先佇列等於沒用,陣列順序不能更改,穩穩吃到 +2
#include<bits/stdc++.h>
#define int long long
using namespace std;
template<typename T>inline void re(T &x)
{
x=0;
int f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
x*=f;
}
template<typename T>inline void wr(T x)
{
if(x<0)putchar('-'),x=-x;
if(x>9)wr(x/10);
putchar(x%10^48);
}
int a[1000000];
int n;
signed main()
{
re(n);
for(int i=2;i<=n;++i)re(a[i]);
int ans=0;
for(register int i=2;i<=n;++i)
ans+=a[i]*i;
wr(ans);
return 0;
}
B
模擬題。類似記憶化,還好 1Y 了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,x,y,d;
const int N=600;
char mp[N][N];
bool vis[N][N][4];
int cx[4]={-1,0,1,0},cy[4]={0,1,0,-1};
inline int getd(int nowd,int x,int y)
{
if(mp[x][y]=='.')return nowd;
else if(mp[x][y]=='/')
{
switch(nowd)
{
case 0:return 1;
case 1:return 0;
case 2:return 3;
case 3:return 2;
}
}
else // '\'
{
switch(nowd)
{
case 0:return 3;
case 1:return 2;
case 2:return 1;
case 3:return 0;
}
}
}
inline int dfs(int x,int y,int d,int s)
{
if(vis[x][y][d])return -1;
if(x>n||x<1||y<1||y>m)return s;
vis[x][y][d]=1;
int tx=x+cx[d],ty=y+cy[d];
return dfs(tx,ty,getd(d,tx,ty),s+1);
}
inline void solve()
{
cin>>n>>m>>x>>y>>d;
for(int i=1;i<=n;++i)
{
getchar();
for(int j=1;j<=m;++j)
mp[i][j]=getchar();
}
int ans=dfs(x,y,d,0);
if(ans==-1)printf("Forever!");
else cout<<ans;
}
signed main()
{
solve();
return 0;
}
C
人類智慧構造題,但好像還是挺好構造的,只不過我想了大概四十分鐘才做出來,還是太廢物了。
#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void re(T &x)
{
x=0;
int f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
x*=f;
}
template<typename T>inline void wr(T x)
{
if(x<0)putchar('-'),x=-x;
if(x>9)wr(x/10);
putchar(x%10^48);
}
int k;
int main()
{
re(k);
int p=0;
for(int i=1;i<=k;++i)
{
printf("%d %d %d\n",p,p+1,p+21);
if(i%2)p+=22;
else p+=41;
}
return 0;
}
D
感覺是一道大概有綠題思維難度的博弈,自己僅僅獨立做出來了 \(\frac{1}{4}\) ,後面看著題解自己思考了一些。
題意
有一個 01 串,長度為 \(n\) ,如果一個 \(1\) 的右邊是 \(0\) 的話,可以向右移動這個 \(1\) ,當一個 \(1\) 在串的最末尾的時候,可以移動它並且得到一分。
現在有 \(A,B\) 兩個玩家,問如果他們都 optimally 地進行操作, 問最後誰的得分更高。
分析
首先來說,對於最右邊的 \(1\) ,如果它距離串的末尾的距離是偶數,那麼當前的人去移動它就肯定是一個必勝態;反之就是一個必敗態,當前的人一定不會去移動它(除非沒有其他的 \(1\) 可以移動)。
1.根據以上理論我們不難知道當一個串末尾為 \(0\) ,且有 \(2\) 個 \(1\) 的時候,一定會出現平局(無論誰先手),也可以推廣偶數個 \(1\) 的情況。
2.當一個串末尾為 \(0\) 且有奇數個 \(1\) 的時候,考慮末尾的 \(1\) 出於必勝態還是必敗態,如果是必勝態,那麼當前的人一定會去移動它,將它變為必敗態,這時候一定不會有人去移動它,而是去交替移動左邊的 \(1\) ,直到達到 \(...111110\) 的情況為止,那麼此時輪到誰先手操作需要由初始態到現在狀態的運算元決定。容易思考出:此時先手的人必定會輸。
3.當一個串末尾為 \(1\) 且有奇數個 \(1\) 的時候,先手必定贏,因為先手得分以後就是平局態。
4.當一個串末尾為 \(1\) 且有偶數個 \(1\) 的時候,先手先得一分,然後變成第二種情況。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
template<typename T>inline void re(T &x)
{
x=0;
int f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
x*=f;
}
template<typename T>inline void wr(T x)
{
if(x<0)putchar('-'),x=-x;
if(x>9)wr(x/10);
putchar(x%10^48);
}
int n;
signed main()
{
re(n);
char c[1000010];
int cnt=0;
for(int i=1;i<=n;++i)
{
c[i]=getchar();
if(c[i]=='1')cnt++;
}
int pos=n-1;
for(;pos>=1;--pos)if(c[pos]=='1')break;
if(c[n]=='0')
{
if(cnt%2==0)printf("Draw");
else//
{
int s=0;
for(int i=pos,idx=n;i;--i)if(c[i]=='1')s+=n-1-i-(n-idx),idx--;
if(s%2)printf("Nigu");
else printf("Frizea");
}
}
else
{
if(cnt%2==1)printf("Nigu");
else
{
int s=0;
for(int i=pos,idx=n;i;--i)if(c[i]=='1')s+=n-1-i-(n-idx),idx--;
if(s%2)printf("Draw");
else printf("Nigu");
}
}
return 0;
}
E
分析
這個是一眼不能貪心,所以就DP了。
圖方便寫了個記憶化,感覺複雜度應該挺小的,結果由於是第一次寫浮點數的DP,咱判斷記憶化的時候好像還不能直接用等於號,於是就 T 了兩三發,索性寫DP了,但是由於邊界和層與層的巢狀關係沒有搞得太清楚,調了很久,最後記憶化也改出來了,DP也寫出來了。
細節
這個題的階段是天數,非常明顯了,然後應該從最後一天倒著列舉。
雖然看著是四層迴圈,但是實際上很小,可以透過。
Code
#include<bits/stdc++.h>
using namespace std;
int n;
double a[500][500],dp[500][500][10],sum[500][500];
inline void pre()
{
cin>>n;
int len=(int)pow(2,n);
for(register int i=1;i<=len;++i)
for(register int j=1;j<=len;++j)
scanf("%lf",&a[i][j]);
for(register int i=1;i<=len;++i)
for(register int j=1;j<=len;++j)
sum[i][j]=sum[i-1][j]+sum[i][j-1]+a[i][j]-sum[i-1][j-1];
}
double calc(int i,int j,int x,int y)
{
return sum[x][y]-sum[x][j-1]-sum[i-1][y]+sum[i-1][j-1];
}
inline double get(int x,int y,int k)
{
if(k>n)return 0.0;
if(dp[x][y][k]>0)
return dp[x][y][k];
//calc
int l=(int)pow(2,n-k);
double ans=0.0,div=pow(2,k);
for(register int i=x;i<=x+l;++i)
for(register int j=y;j<=y+l;++j)
ans=max(ans,calc(i,j,i+l-1,j+l-1)/div+get(i,j,k+1));
return dp[x][y][k]=ans;
}
inline void memorized_search()
{
int len=(int)pow(2,n);
for(register int i=1;i<=len;++i)
for(register int j=1;j<=len;++j)
for(register int k=1;k<=n;++k)
dp[i][j][k]=-1.0;
printf("%.1lf",sum[len][len]-get(1,1,1));
}
inline void DP()
{
int len=(int)pow(2,n);
for(register int k=n;k>=1;--k)
{
int l=(int)pow(2,n-k+1);
for(register int i=1;i+l-1<=len;++i)
{
for(register int j=1;j+l-1<=len;++j)
{
for(register int u=i;u<=i+l/2;++u)
for(register int v=j;v<=j+l/2;++v)
dp[i][j][k]=max(dp[i][j][k],(dp[u][v][k+1]+calc(u,v,u+l/2-1,v+l/2-1))/2.0);
}
}
}
printf("%.1lf",sum[len][len]-dp[1][1][1]);
}
int main()
{
pre();
DP();
// memorized_search();
return 0;
}
F
人類智慧題,不多贅述,吃罰時就是了。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
template<typename T>inline void re(T &x)
{
x=0;
int f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
x*=f;
}
template<typename T>inline void wr(T x)
{
if(x<0)putchar('-'),x=-x;
if(x>9)wr(x/10);
putchar(x%10^48);
}
int T;
bool check(int a,int b,int c)
{
int t=0;
if(a==0)t++;
if(c==0)t++;
if(b==0)t++;
return t<=1;
}
signed main()
{
re(T);
while(T--)
{
int a,b,c;
re(a),re(b),re(c);
if(b==c)puts("Yes");
else if(!check(a,b,c))puts("No");
else
{
if(b>c)swap(b,c);
if((c-b)%3!=0)puts("No");
else puts("Yes");
}
}
return 0;
}
G
模擬題 \(O(t\sqrt n)\) 可以透過
#include<bits/stdc++.h>
using namespace std;
inline void check(int x)
{
int i;
int ans=0;
for(i=1;i*i<x;++i)
if(x%i==0)ans+=2;
if(i*i==x)ans++;
if(x%ans==0)puts("YES");
else puts("NO");
}
int main()
{
int T;
cin>>T;
while(T--)
{
int n;
cin>>n;
check(n);
}
return 0;
}
H
模擬題
Code
#include<bits/stdc++.h>
using namespace std;
inline void solve(string s)
{
cin>>s;
int len=s.length();
if(s[5]=='l')
{
for(register int i=9;i<=len-4;++i)putchar(s[i]);
putchar('\n');
}
else
for(register int i=7;i<=len-4;++i)putchar(s[i]);
}
int main()
{
int L;
cin>>L;
L-=2;
string s;
cin>>s;
while(L--)
solve(s);
cin>>s;
return 0;
}
I
乍一眼看是個DP,結果發現好像不能重複,看了題解發現是個二分加貪心,寫完了還是過不了,但倒是可以過樣例,鴿了,不想改這題了。
Code
#include<bits/stdc++.h>
using namespace std;
char tmp[1000100],s[1000100];
int all_i,len;
inline void pre()
{
cin>>s;
len=strlen(s);
for(register int i=0;i<len;++i)
if(s[i]=='I')all_i++;
}
inline bool check(int x)
{
int remain_I=0,remain_O=0,finished_I=0,find_I=0;
for(register int i=len-1;i>=0;--i)
{
if(s[i]=='I')
{
if(find_I!=x)find_I++,remain_I++;
else if(remain_O)remain_I--,remain_O--,finished_I++;
}
if(s[i]=='O'&&remain_I!=0)remain_O++;
if(s[i]=='N'&&remain_O!=0)remain_I--,remain_O--,finished_I++;
}
return finished_I==x;
}
inline int bs(int l,int r)
{
if(l==r)return l;
int mid=(l+r+1)>>1;
if(check(mid))return bs(mid,r);
else return bs(l,mid-1);
}
int main()
{
pre();
cout<<bs(0,len);
return 0;
}
//IOIOIOIONIOIOI