2019山东省赛
A.ZOJ4113 Calandar
题意:
题目中一年有12个月,每个月都是30天,一周五天。现在给你一个日期和当天是星期几,要你求给出的第二个日期是星期几。
题解:
因为每个月都是30天,每周都是5天,所以每个月同一号对应的星期几都是相同的。比如1年1月1日是星期一,不管是哪年哪月的1号都是星期一。所以只要计算两个日期的日这一栏相差多少输出对应的星期几就行了。
代码:
#include <cstdio> #include <cstring> #define ll long long #define ull unsigned long long using namespace std; const int N = 200000 + 5; const ll INF = 1e18; int T; char xq[5][30] = {"Monday","Tuesday","Wednesday","Thursday","Friday"}; int main() { for (scanf("%d",&T); T--;) { int y1,m1,d1,y2,m2,d2; char s[30]; scanf("%d%d%d%s%d%d%d",&y1,&m1,&d1,s,&y2,&m2,&d2); int pos; for (int i = 0; i < 5; i++) { if (strcmp(xq[i],s) == 0) { pos = i; break; } } if (d1<=d2) { printf("%s\n",xq[(pos+(d2-d1))%5]); }else { printf("%s\n",xq[(pos+30 - (d1-d2))%5 ]); } } return 0; }
B.ZOJ4114 Flipping Game
题意:
有n个开关对应n盏灯,按顺序给出每盏灯的初始状态和目标状态,游戏有k轮,每轮可以按m个不同的开关各一次。问你方案数。
题解:
一看题就想到用组合数,但是没想好怎么计算。
我们可以用一个二维dp[i][j] ,表示第i轮有j个状态与目标状态不同的灯的方案数。每一轮我们可以选x个与目标状态不同的灯,y个与目标状态相同的灯:x ≤ j,y ≤ n-j,x+y = m,按了之后与目标状态不同的灯的数量为 j - x + y。
那么可以得到状态转移方程为:dp[i+1][j-x+y] = dp[i+1][j-x+y] + dp[i][j]*C(j,x)*C(n-j,y)
代码:
#include <cstdio> #include <cstring> #include <algorithm> #define ll long long #define ull unsigned long long using namespace std; const int N = 100 + 5; const ll mod = 998244353; const ll INF = 1e18; int T; char s1[N],s2[N]; ll inv[N],fac[N],inv_fac[N],dp[N][N]; void init() { inv[0]=inv[1]=inv_fac[0]=fac[0]=1; for(int i=2; i<N; i++) inv[i]=inv[mod%i]*(mod-mod/i)%mod; for(int i=1; i<N; i++) fac[i]=fac[i-1]*i%mod; for(int i=1; i<N; i++) inv_fac[i]=inv_fac[i-1]*inv[i]%mod; } ll C(int n,int m) { return fac[n]*inv_fac[m]%mod*inv_fac[n-m]%mod; } int main() { init(); for (scanf("%d",&T); T--;) { int n,m,k,num=0; scanf("%d%d%d%s%s",&n,&k,&m,s1,s2); for (int i = 0; i < n; ++i) { if(s1[i] != s2[i]) num++; } memset(dp,0,sizeof(dp)); dp[0][num] = 1; for (int i = 0; i < k; ++i) { for (int j = 0; j <= n; ++j) { if (!dp[i][j]) continue; for (int x = 0; x <= j; ++x) { int y = m - x; if (y<0||y>(n-j)) continue; dp[i+1][j-x+y] = (dp[i+1][j-x+y]+dp[i][j]*C(j, x)%mod*C(n-j,y)%mod)%mod; } } } printf("%lld\n",dp[k][0]); } return 0; }
C:ZOJ4115 Wandering Robot
题意:
有一个机器人,可以上下左右走,它一开始在(0,0),给你一个由UDLR组成的长度为n的序列,表示机器人走的路线,机器人沿这个路线走k次。问你机器人从第1步到n*k步的过程中离原点曼哈顿距离最大是多少。
题解:
我们可以知道这个过程中离原点曼哈顿距离最大的点不是在第1轮就是在第k轮(没考虑在第一轮wa了),在第一轮时每走一步就判断一下最远曼哈顿距离,走完一轮后的点乘以k-1就得到了最后一轮的起点,然后再从这个点开始走第k轮,每一步判断一下。
代码:
#include <cstdio> #include <cstring> #include <algorithm> #define ll long long #define ull unsigned long long using namespace std; const int N = 1e5 + 5; const ll INF = 1e18; int T; char s[N]; int main() { for (scanf("%d",&T); T--;) { int n; ll k,ex=0,ey=0,maxd=0; scanf("%d%lld%s",&n,&k,s); for (int i = 0; i < n; ++i) { if (s[i]=='U') ey++; if (s[i]=='D') ey--; if (s[i]=='L') ex--; if (s[i]=='R') ex++; maxd=max(maxd,abs(ex)+abs(ey)); } k--; ex *= k; ey *= k; maxd=max(maxd,abs(ex)+abs(ey)); for (int i = 0; i < n; ++i) { if (s[i]=='U') ey++; if (s[i]=='D') ey--; if (s[i]=='L') ex--; if (s[i]=='R') ex++; maxd=max(maxd,abs(ex)+abs(ey)); } printf("%lld\n",maxd); } return 0; }
D:ZOJ4116 Game on a Graph
题意:
有k个人,编号为0~k-1,分1、2两个阵营。有一个n个点m条边的无向连通图,k个人从0号开始依次从图中删一条边,谁删了边之后使这个图不连通,那么对方阵营就赢了,输出赢的阵营。
题解:
n个点的无相连通图最少有n-1条边,所以谁删了边之后只剩n条边,那么对方阵营胜利。
代码:
#include <cstdio> #include <cstring> #include <algorithm> #define ll long long #define ull unsigned long long using namespace std; const int N = 1e5 + 5; const ll INF = 1e18; int T; char s[N]; int main() { for (scanf("%d",&T); T--;) { int n,m,k,u,v; scanf("%d%s%d%d",&k,s,&n,&m); for (int i = 0; i < m; i++) scanf("%d%d",&u,&v); if (s[(m-n+1)%k] == '1') printf("2\n"); else printf("1\n"); } return 0; }
F:ZOJ4118 Stones in the Bucket
题意:
有n堆石子,每堆石子有ai个,你现在可以任选一堆非空的拿掉一个石子或者给其他堆一个石子,问最少需要进行几次操作使得每堆石子数量相等。
题解:
显然最后每堆石子的数量都是sum/n(整数除),我们只要计算每一堆要减去的和就行了。哪一堆少了的话可以从多了的补。
代码:
#include <cstdio> #define ll long long using namespace std; const int N = 1e5 + 5; const ll INF = 1e18; int T; ll a[N]; int main() { for (scanf("%d",&T); T--;) { int n; ll sum = 0; scanf("%d",&n); for (int i = 0; i < n; ++i) { scanf("%lld",&a[i]); sum += a[i]; } sum/=n; ll num1 = 0; for (int i = 0; i < n; i++) { if (sum < a[i]) num1 += a[i]-sum; } printf("%lld\n",num1); } return 0; }
M:ZOJ4125 Sekiro
题意:
给你一个n,每次减去它的一半(向下取整),问执行k次之后n是多少
题解:
如果n是0,那么结果就是0,否则n最多减32,它最后变为1,1-1/2=1。直接暴力算就行了。
代码:
#include <cstdio> #include <cstring> #define ll long long #define ull unsigned long long using namespace std; const int N = 200000 + 5; const ll INF = 1e18; int T; int main() { for (scanf("%d",&T); T--;) { int n,k; scanf("%d%d",&n,&k); for (int i = 0; i < k; ++i) { n -= n/2; if (n==0||n==1) break; } printf("%d\n",n); } return 0; }