A-zzy的小号
/*****************************
Author Ms. Wen
Date 2018/4/6
解题思路:
i,l,I,L 等价 遇到任意一个*4
o,O,0 等价 遇到任意一个*3
大小写字母等价 遇到任意一个*2
*******************************/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int mod = 1e9+7;
char str[1000000];
int main() {
while(~scanf("%s",str)) {
long long ans = 1;
for(int i = 0; i < (int)strlen(str); i++) {
if(str[i]>='a' && str[i]<='z') {
if(str[i]=='l' || str[i]=='i') {
ans = ans*4%mod;
}
else if(str[i]=='o') {
ans = ans*3%mod;
}
else {
ans = ans*2%mod;
}
}
else if(str[i]>='A'&&str[i]<='Z') {
if(str[i]=='I' || str[i]=='L') {
ans = ans*4%mod;
}
else if(str[i]=='O') {
ans = ans*3%mod;
}
else {
ans = ans*2%mod;
}
}
else if(str[i] == '0'&&str[i]<='9') {
ans = ans*3%mod;
}
}
printf("%lld\n",ans);
}
return 0;
}
B-Jxc的军训
/*****************************
Author Ms. Wen
Date 2018/4/6
解题思路:
逆向思维,被晒到的概率等于1-不被晒到的概率。
只要站在云下不管太阳在哪都不会被晒到。则
不被云晒到的概率为 m/(n*n)。则被晒到的概率是
(n*n-m)/(n*n)。然后剩余的就是求乘法逆元的事情
了,由于取模的数为素数,因此分母fm的乘法逆元
就是fm^(mod-2)。在题目中我们经常看到概率的答案
很大,其实就是因为这些概率通常牵涉取模,与乘法
逆元有关,不必大惊小怪。
*******************************/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cmath>
using namespace std;
const int mod = 998244353;
long long qucik(int n,int k) {
long long ans,res;
ans = 1;
res = (long long)n;
while(k) {
if(k&1) {
ans = ans*res%mod;
}
res = res*res%mod;
k = k/2;
}
return ans;
}
int main() {
int n,m;
while(~scanf("%d%d",&n,&m)){
int fz = (n*n-m)%mod;
int fm = (n*n)%mod;
long long ans = fz*qucik(fm,mod-2)%mod;
printf("%lld\n",ans);
}
return 0;
}
C-zzf的好矩阵
/*****************************
Author Ms. Wen
Date 2018/4/6
解题思路:
比如3*3的矩阵,可以通过一定操作放成
1 2 3
4 5 6
7 8 9
由于矩阵中的数字大于0且小于等于n*n,且各个
位置上数不同。则矩阵中的数字必是1~n*n中的数字
各一个。经过烟草发现。1 4 7,2 3 8,3 6 9 .
必是一列。1 2 3,4 5 6,7 8 9,必是一列,因此只要
固定1,2,3的位置,与其绑定的数字必绑定。
1 ,2 ,3的排列数都3!种。横排的数还可以挑选自己
所处的行,又是3!种。而上例中所有在一行的可以换
成在一列。
1 4 7
2 5 8
3 6 9
因此n*n的矩阵,总共有n!*n!*2种放置方法。
*******************************/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <cmath>
using namespace std;
const int mod = 998244353;
typedef long long ll;
int main() {
int p;
while(~scanf("%d",&p)) {
ll ans = 1;
for(ll i = 1; i <= p; i++) {
ans = ans*i%mod;
}
ans = ans*ans%mod;
ans = ans*2%mod;
printf("%lld\n",ans);
}
return 0;
}
D-applese的生日
/***************************************************************
Author Ms. Wen
Date 2018/4/6
思路:题解上说得挺清楚。
首先有一个结论:每块蛋糕分成的每一块的大小是相同的 基于这个结论,
每次找到当前划分最大块所在的大块并将其划分数+1,检查是否满足题目的
要求考虑这个结论为什么是正确的考虑一个蛋糕切的刀数不变,那么可以想
到假如分割得到的块是不同的,那么可能的贡 献是增加最大和最小之间的差
值,那么这样答案只会更劣,所以可以想到分成的每个块的大小是相同的。
*******************************************************************/
#include <iostream>
#include <stdio.h>
#include <map>
using namespace std;
const int maxn = 1005;
double w[maxn];
int divs[maxn]; //蛋糕的分块数。
multimap<double,int>m; //multimap中key的值可以重复
multimap<double,int>::iterator it1,it2;
int main() {
double t;
int n;
cin>>t>>n;
m.clear();
for(int i = 1; i <= n; i++) {
cin>>w[i];
divs[i] = 1;
m.insert(make_pair(w[i],i));
}
it1 = m.begin();
it2 = --m.end();
int ans = 0;
while(it1->first/it2->first < t) {
int id = it2->second;
divs[id]++;
double aver = w[id]/divs[id];
m.erase(it2);
m.insert(make_pair(aver,id));
it1 = m.begin();
it2 = --m.end();
ans++;
}
printf("%d\n",ans);
return 0;
}
E-VVQ与线段
/**************************************
Author Ms. Wen
Date 2018/4/7
解题思路:
对于所有线段按照左端点从小到大排序。
对于一条选定的线段x1,y1。需要考虑左端点大于等于x1且小于等于y1的
所有线段x2,y2
1.考虑Seg(x2,y2)与Seg(x1,y1)相交但不包含在Seg(x1,y1)中,则异或值
为(x2+y2)-(x1+y1).由于x1+y1固定,则需要使x2+y2最大。因此建立线段
树维护所有线段left+right的最大值。
2.考虑Seg(x2,y2)包含在Seg(x1,y1)中,则异或值为(y1-x1)-(y2-x2)。
由于y1-x1固定,则需要使y2-x2最小,因此建立线段树维护所有线段right-left
的最小值。
求这两种情况的最大值。即为答案。
变量含义:
Max:维护线段left+right最大值的数组
Min:维护线段right-left最小值的数组
s:存放线段信息
方法含义:
push_up1(root):用于更新Max数组的值
push_up2(root):用于更新Min数组的值
build1:构造维护left+right的线段树
build2:构造维护right-left的线段树
query1:求与当前线段相交的线段中left+right值最大的。
query2:求与包含在当前线段中的线段中right-left值最小的。
**********************************************************/
#include <iostream>
#include <stdio.h>
#include <algorithm>
#define lchild left,mid,root<<1
#define rchild mid+1,right,root<<1|1
using namespace std;
const int maxn = 200000+4;
int Max[maxn<<2]; //维护线段l+r的最大值
int Min[maxn<<2]; //维护线段r-l的最小值
struct Segment {
int left;
int right;
friend bool operator<(Segment s1,Segment s2) {
return s1.left<s2.left;
}
}s[maxn];
void push_up1(int root) {
Max[root] = max(Max[root<<1],Max[root<<1|1]);
}
void push_up2(int root) {
Min[root] = min(Min[root<<1],Min[root<<1|1]);
}
//维护l+r的最大值,处理相交情况
void build1(int left,int right,int root) {
if(left==right) {
Max[root] = s[left].left + s[left].right;
return;
}
int mid = (left+right)>>1;
build1(lchild); //递归构建左右子树
build1(rchild);
push_up1(root);
}
//维护r-l的最小值,处理包含的情况
void build2(int left,int right,int root){
if(left==right) {
Min[root] = s[left].right - s[left].left;
return;
}
int mid = (left+right)>>1;
build2(lchild);
build2(rchild);
push_up2(root);
}
int query1(int L,int R,int left,int right,int root) {
if(L<=left && right<=R) {
return Max[root];
}
int mid = (left+right)>>1;
int ans = 0;
if(L<=mid) ans = max(ans,query1(L,R,lchild));
if(R>mid) ans = max(ans,query1(L,R,rchild));
return ans;
}
int query2(int L,int R,int left,int right,int root) {
if(L<=left && right<=R) {
return Min[root];
}
int mid = (left+right)>>1;
int ans = 1e8;
if(L<=mid) ans = min(ans,query2(L,R,lchild));
if(R>mid) ans = min(ans,query2(L,R,rchild));
return ans;
}
int main() {
int n,l,r;
while(~scanf("%d",&n)) {
for(int i = 1; i <= n; i++) {
scanf("%d%d",&l,&r);
s[i].left = min(l,r); //防止有l>r的坑
s[i].right = max(l,r);
}
sort(s+1,s+n+1); //按照左端点,从小到大排序。
build1(1,n,1);
build2(1,n,1);
Segment tmp;
tmp.right = 0;
int ans = 0;
for(int i = 1; i <= n; i++) {
tmp.left = s[i].left;
l = lower_bound(s+1,s+n+1,tmp)-s; //寻找左端点>=当前线段的第一条线段
tmp.left = s[i].right;
r = upper_bound(s+1,s+n+1,tmp)-s-1; //寻找左端点>当前右端点的第一条线段。
if(l > r) continue;
//则下标(l,r)内的线段都与当前线段相交或被包含其中。
int num1 = query1(l,r,1,n,1);
int num2 = query2(l,r,1,n,1);
ans = max(ans,num1-(s[i].left+s[i].right));
ans = max(ans,(s[i].right-s[i].left)-num2);
}
printf("%d\n",ans);
}
return 0;
}