此文为博主原创,转载...转载这种文章有意思吗qwq
严禁各OJ在未经博主同意的情况下擅自使用博文中的题面作为OJ题目。
今天做的是我校dalao@wzy出的套题w【被长达6页的题面淹没】
dalao的题就是难...T1就懵逼了...3.5h不知道自己都干了些什么...
最后80+30+0+0 rank7滚粗。
感觉这次题好多人都翻车了,我这么菜到底是怎么rank7 的QAQ
NOIP2017 RP ++
A.谜团
题目大意:
谜团在第一秒会转化一个“虚灵”,之后的时间内将不再转化单位。新
产生的“虚灵”会在下一秒分裂出m 个“虚灵”,原有的“虚灵”仍然存在,并且不再产生虚灵。
新产生的“虚灵”可在下一秒分裂,之后也不再产生“虚灵”。
给定时间 t 和分裂数m,问在 t 秒后,谜团拥有多少个“虚灵”。答案可能很大,要求你模一个数k(不保证k是质数)。
数据范围:
1<=k<=10^8,1<=m<=100,1<=t<=2^31-1
分析:
这题的部分分范围我没有贴...(你什么时候贴过部分分范围啊喂!)
经过推导可以发现,题目的答案其实就是对首项为1,公比为m的等比数列求和。答案为S(t-1)。
直接暴力做的话可以拿40分。
部分分中有一部分是保证m与k互质的,此时可以用等比数列求和公式+逆元,加上暴力部分的数据分治,80分。
满分做法:S(n+m)=S(n)+S(m)*qn
sum(p,c)表示计算 p^0 + p^1 + ... + p^c
如果c是偶数,答案就是 p^0 + p^1 + ... + p^(c/2 - 1) * (1 + 2^(c/2)) + p^c
展开得到 p^0 + p^1 + ... + p^(c/2 - 1) + p^(c/2) + p^(c/2+1) + ... + p^c
如果c是奇数 那么答案就是 p^0 + p^1 + ... + p^(c/2) * (1 + 2^(c/2 + 1))
展开得到 p^0 + p^1 + ... + p^(c/2) + p^(c/2 + 1) + ... + p^c
用一个递归函数计算即可。
AC代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 #include<cstring> 5 6 inline void read(long long &x) 7 { 8 char ch = getchar(),c = ch;x = 0; 9 while(ch < ‘0‘ || ch > ‘9‘) c = ch,ch = getchar(); 10 while(ch <= ‘9‘ && ch >= ‘0‘) x = (x<<1)+(x<<3)+ch-‘0‘,ch = getchar(); 11 if(c == ‘-‘) x = -x; 12 } 13 14 long long k,m,t,ans; 15 //S(n+m) = S(n)+S(m)*q^n 16 long long ksm(int b) 17 { 18 long long base = m,r = 1; 19 for(;b;b>>=1) 20 { 21 if(b&1) r = r*base%k; 22 base = base*base%k; 23 } 24 return r; 25 } 26 27 long long calc(int t) 28 { 29 if(t == 0) return 1; 30 if(t&1) return (calc(t/2)*(1+ksm(t/2+1))%k)%k; 31 else return (calc(t/2-1)*(1+ksm(t/2))%k+ksm(t))%k; 32 } 33 34 int main() 35 { 36 read(m),read(t),read(k); 37 printf("%lld\n",calc(t-1)%k); 38 return 0; 39 }
mituan
B.祈求者
题目大意:
有一个长度为k的字符串和n个字符集合。你可以从每个字符集合中选择至多1个字符,使它们拼成
字符串的前缀。求能够拼出的最长前缀。
输入的第一个字母为T,表示接下来有T组数据。
数据范围:
1<=T<=20,1<= Σai+n <= 200,1 <= k <= 200.
分析:
如果把题目改成这样:
【从每个字符集合中选择至多一个字符,尽量多地、连续地匹配前缀】
是不是很容易就想到了二分图匹配呀。
最大流也可做,不过当时这题有dalao用费用流拿了100分,向dalao低头www
如果1~mid能够匹配,则1~mid-1也一定能匹配;1~mid不能完全匹配,1~mid+1也不能完全匹配,可以二分。
二分能够匹配的前缀的最长长度,对于每次二分出的长度,做一次二分图匹配。
如果匹配数==二分的长度,说明该长度是可行的。
AC代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 #include<cstring> 5 6 inline void read(int &x) 7 { 8 char ch = getchar(),c = ch;x = 0; 9 while(ch < ‘0‘ || ch > ‘9‘) c = ch,ch = getchar(); 10 while(ch <= ‘9‘ && ch >= ‘0‘) x = (x<<1)+(x<<3)+ch-‘0‘,ch = getchar(); 11 if(c == ‘-‘) x = -x; 12 } 13 14 int t,n,l,r,mid,ans,cnt,len,tmp; 15 int head[6000],link[500],vis[500]; 16 char s[202],glo[202][202]; 17 18 struct Edge 19 { 20 int f,t,nxt; 21 }e[6000]; 22 23 void insert(int f,int t) 24 { 25 e[++cnt].f = f,e[cnt].t = t; 26 e[cnt].nxt = head[f]; 27 head[f] = cnt; 28 } 29 30 bool dfs(int now) 31 { 32 for(int i = head[now];i;i = e[i].nxt) 33 { 34 int to = e[i].t; 35 if(vis[to]) continue; 36 vis[to] = 1; 37 if(!link[to] || dfs(link[to])) 38 { 39 link[to] = now; 40 return true; 41 } 42 } 43 return false; 44 } 45 46 bool check(int x) 47 { 48 memset(link,0,sizeof(link)); 49 memset(head,0,sizeof(head)); 50 memset(e,0,sizeof(e));cnt = 0; 51 int res = 0; 52 for(int l = 1;l <= x;++ l) 53 for(int i = 1;i <= n;++ i) 54 { 55 tmp = strlen(glo[i]+1); 56 for(int j = 1;j <= tmp;++ j) 57 if(glo[i][j] == s[l]) 58 insert(l,i); 59 } 60 for(int i = 1;i <= x;++ i) 61 { 62 memset(vis,0,sizeof(vis)); 63 if(dfs(i)) res ++; 64 } 65 // printf("%d %d\n",cnt,r); 66 if(res == x) return true; 67 else return false; 68 } 69 70 int main() 71 { 72 // freopen("kaer.in","r",stdin); 73 // freopen("kaer.out","w",stdout); 74 freopen("1.txt","r",stdin); 75 read(t); 76 while(t -- ) 77 { 78 read(n); 79 for(int i = 1;i <= n;++ i) 80 scanf("%s",&glo[i][1]); 81 scanf("%s",s+1); 82 len = strlen(s+1); 83 l = 1,r = len,ans = 0; 84 while(l <= r) 85 { 86 mid = (l+r)>>1; 87 if(check(mid)) l = mid+1,ans = mid; 88 else r = mid-1; 89 } 90 printf("%d\n",ans); 91 } 92 return 0; 93 }
kaer
C.大地之灵
题目大意:
有三种颜色点,不同颜色的点之间可以任意连边,同色的点之间必须间隔至少2个其他颜色的点才能连边。
给出这三种点的个数n,m,k,求连边的方案数。答案%1e9+7。
数据范围:
1 <= n,m,k <= 5000
分析:
先考虑有两种点的情况。由题意可知,此时每个点的出度最多为1,边数最多为Min(a,b)。
把边分别编号为1,2,3...n,进行DP。
dp[i]表示连了i条边的方案数。转移方程:dp[i] = dp[i-1]*(a-i+1)*(b-i+1)/i
转移比较容易想到,但是为什么要除以i可能不容易理解。
因为连的边是无序的,但是为了方便DP,你给边钦定了顺序。
这样一来就会导致:完全相同的两条边,编号为1,2时计算一次,编号为2,1时计算一次。
最终答案为Σdp[i]
同理,当有三种颜色时,只需要把这个过程做三次,乘起来即可。
AC代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 #include<cstring> 5 6 const int MOD = 1e9+7; 7 inline void read(int &x) 8 { 9 char ch = getchar(),c = ch;x = 0; 10 while(ch < ‘0‘ || ch > ‘9‘) c = ch,ch = getchar(); 11 while(ch <= ‘9‘ && ch >= ‘0‘) x = (x<<1)+(x<<3)+ch-‘0‘,ch = getchar(); 12 if(c == ‘-‘) x = -x; 13 } 14 15 int n,m,k; 16 long long ans,dp[25000001]; 17 18 inline int Max(int a,int b) 19 {return a>b?a:b;} 20 21 long long calc(int a,int b) 22 { 23 long long r = 0; 24 for(int i = 1;i <= 25000000;++ i) 25 if(!dp[i]) break; 26 else dp[i] = 0; 27 dp[0] = 1; 28 int mx = Max(a,b); 29 for(int i = 1;i <= mx;++ i) 30 dp[i] = (dp[i-1]*(a-i+1)*(b-i+1)/i)%MOD; 31 for(int i = 0;i <= mx;++ i) 32 r = (r+dp[i])%MOD; 33 return r; 34 } 35 36 int main() 37 { 38 read(n),read(m),read(k); 39 ans = calc(n,m)*calc(n,k)%MOD *calc(m,k)%MOD; 40 printf("%lld\n",ans); 41 return 0; 42 }
ES
D.裂魂人
题目大意:
给你一张n 个点,m 条边的无向图(没有重边,没有自环),每条边有一个边权w。
输出起点为S,终点为E,且恰好经过k 条边的最短路长度。
不保证起点和终点不是同一个点,保证有解。点的编号为1,2,...,n
数据范围:
3 <= n <= 200, 2 <= m <= 200,1 <= w <= 1000,k<= 1,000,000
分析:
这什么蛇皮题目!我不做辣!
正解:Floyd倍增+矩阵快速幂加速DP
反正我是不会的qwq读者有意向的话可以问@wzydalao