T1远征(expedition)
【题目描述】
寒枫将军将要带领他的部队去圣雪山消灭那里的冰龙。
部队分成了若干个小队,属于同一个小队的人兵种相同。寒枫将军有着杰出的指挥能力,在战斗的时候,寒枫将军能够让所有相同兵种的人互相配合,使t个相同兵种的人发挥出t2的战斗力;寒枫将军还能让不同兵种的人互相配合,使整个部队的战斗力是所有兵种战斗力的和。
例如,部队中有3个小队,分别是5个人的步兵小队,3个人的步兵小队,3个人的骑兵小队。那么步兵战斗力为64,骑兵战斗力为9,部队总战斗力为73。
寒枫将军需要知道他的部队的战斗力是多少。
【输入格式】
第一行一个整数n,表示小队数。接下来n行,第i行有两个整数ai、bi,表示这个小队有ai个人,兵种为bi。
【输出格式】
一行一个整数,部队的战斗力。
【样例输入】
3
5 1
3 1
3 2
【样例输出】
73
【数据规模与约定】
10%的数据,n=1
30%的数据,n≤1000
另有20%的数据,ai=1
另有30%的数据,bi≤1000
100%的数据,1≤n≤100000,1≤ai≤10000,1≤bi≤1,000,000,000
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #define ll long long 6 using namespace std; 7 const int mod=9979,N=100100; 8 ll tot,head[mod+5],nxt[N],edge[N],num[N],n;//num记录每个兵种的人数 9 inline ll read(){ 10 ll res=0; 11 char ch=getchar(); 12 while(ch<'0' || ch>'9'){ 13 ch=getchar(); 14 } 15 while(ch>='0'&&ch<='9'){ 16 res=res*10+ch-'0'; 17 ch=getchar(); 18 } 19 return res; 20 } 21 void add(ll a,ll b){//兵种 和 人数 22 ll x=a&mod; 23 for(ll i=head[x];i;i=nxt[i]){ 24 if(edge[i]==a){ 25 num[i]+=b; 26 return ; 27 } 28 } 29 edge[++tot]=a; 30 num[tot]=b; 31 nxt[tot]=head[x]; 32 head[x]=tot; 33 } 34 ll ans=0; 35 void cal(){ 36 for(ll i=0;i<=mod;i++){ 37 if(!head[i])continue; 38 for(ll j=head[i];j;j=nxt[j]){ 39 ans=ans+num[j]*num[j]; 40 } 41 } 42 } 43 int main() 44 { 45 freopen("expedition.in","r",stdin); 46 freopen("expedition.out","w",stdout); 47 n=read(); 48 int a,b; 49 for(register int i=1;i<=n;i++){ 50 a=read();b=read(); 51 add(b,a); 52 } 53 cal(); 54 printf("%lld",ans); 55 return 0; 56 }
看数据范围hin吓人 其实这题不写高精也可以的!
T2密码(substring)
【问题描述】
假发通过了不懈的努力,得到了将军家门锁的密码(一串小写英文字母)。但是假发被十四和猩猩他们盯上了,所以假发需要把密码传递出去。因为假发不想十四他们发现几松门前贴的小纸条就是将军家的密码,所以他加密了密码(新八:听起来有点诡异)。加密方法如下:随机地,在密码中任意位置插入随机长度的小写字符串。
不过,假发相信银桑和他那么多年小学同学,一定能猜中密码是什么的(新八:银桑什么时候成攮夷志士了!!!)。可是,写完了小纸条之后,假发觉得有点长,就想截去头和尾各一段(可以为空),让剩下的中间那一段依然包含真~密码。想着想着,假发就想知道有多少种可行方案。结果在沉迷于稿纸之际,假发被投进了狱门岛(新八:……)。于是,就由你计算了。
【输入】
两行非空字符串,纯小写英文字母,第一行是加密后的密码,第二行是原密码。
第一行长度不超过300000,第二行不超过200。
【输出】
一行,有多少种方案。注意:不剪也是一种方案。
【输入输出样例1】
substring.in |
substring.out |
abcabcabc cba |
9 |
【样例1解释】
用(L,R)表示一种方案,其中L和R分别表示截去头和尾的长度。这9钟方案分别是(0,0),(0,1),(0,2),(1,0),(1,1),(1,2),(2,0),(2,1),(2,2)。
【输入输出样例2】
substring.in |
substring.out |
abcabcaba cba |
12 |
【输入输出样例3】
substring.in |
substring.out |
abcabcabac cba |
18 |
【数据说明】
30%的数据满足第一行长度不超过1000。
100%的数据满足第一行长度不超过300000,方案总数不超过10^18。
第二行长度小于213
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 using namespace std; 6 char s1[300010],s2[220]; 7 int nxt[30][300010]; 8 int p[30]; 9 long long ans=0; 10 int main(){ 11 freopen("substring.in","r",stdin); 12 freopen("substring.out","w",stdout); 13 cin>>s1+1>>s2+1; 14 int len1=strlen(s1+1); 15 int len2=strlen(s2+1); 16 memset(p,-1,sizeof(p)); 17 //cout<<len1<<len2<<endl; 18 for(int i=len1;i>=1;i--){ 19 for(int j=0;j<26;j++){ 20 nxt[j][i]=p[j];//记录位置 21 } 22 p[s1[i]-'a']=i; 23 } 24 int l,r; 25 int x=p[s2[1]-'a'];//找到初始位置 26 int pl=0; 27 while(x!=-1){ 28 bool flag=1; 29 l=x; 30 for(int i=2;i<=len2;i++){ 31 if(nxt[s2[i]-'a'][x]!=-1) 32 x=nxt[s2[i]-'a'][x]; 33 else{ 34 flag=0; 35 break;//没找到 36 } 37 } 38 if(flag){ 39 r=x; 40 ans+=(l-pl)*(len1-r+1); 41 pl=l; 42 x=nxt[s2[1]-'a'][l]; 43 } 44 else break; 45 } 46 printf("%lld",ans); 47 return 0; 48 }
我们先预处理字符串s1 求出当前结点到下一个相同字母结点的编号 这个要逆序去寻找
其次我们依次匹配 匹配到(匹配到的子串满足从某点出发是最短的匹配串)一次就更新答案
不能再匹配了就跳出结束
ans+=(l-pl)*(len-r)
l为当前左端点 pl为上一次匹配的左端点len就是长度 r为右端点
T3独立集(bubble)
【问题描述】
有一天,一个名叫顺旺基的程序员从石头里诞生了。又有一天,他学会了冒泡排序和独立集。在一个图里,独立集就是一个点集,满足任意两个点之间没有边。于是他就想把这两个东西结合在一起。众所周知,独立集是需要一个图的。那么顺旺基同学创造了一个算法,从冒泡排序中产生一个无向图。
这个算法不标准的伪代码如下:
Pascal版本 |
C/C++版本 |
procedure bubblesortgraph(n, a[]) : /*输入:点数n,1到n的全排列a。 输出:一个点数为n的无向图G。*/ 创建一个有n个点,0条边的无向图G。 repeat swapped = false for i 从 1 到 n-1 : if a[i] > a[i + 1] : 在G中连接点a[i]和点a[i + 1] 交换a[i]和a[i + 1] swapped = true until not swapped 输出图G。 //结束。 |
void bubblesortgraph(n,a[]) //输入:点数n,1到n的全排列a //输出:一个点数为n的无向图G { 创建一个有n个点,0条边的无向图G。 do{ swapped=false for i 从1 到n-1 if(a[i]>a[i+1]) { 在G中连接点a[i]和点a[i+1] 交换a[i]和a[i+1] swapped =true } }while(swapped); 输出图G。 } //结束。 |
那么我们要算出这个无向图G最大独立集的大小。但是事情不止于此。顺旺基同学有时候心情会不爽,这个时候他就会要求你再回答多一个问题:最大独立集可能不是唯一的,但有些点是一定要选的,问哪些点一定会在最大独立集里。今天恰好他不爽,被他问到的同学就求助于你了。
【输入】
输入包含两行,第一行为N,第二行为1到N的一个全排列。
【输出】
输出包含两行,第一行输出最大独立集的大小,第二行从小到大输出一定在最大独立集的点的编号。
【输入输出样例】
bubble.in |
bubble.out |
3 3 1 2 |
2 2 3 |
【样例说明】
如上图,顶点1和2一定在最大独立集中,其对应的编号为2和3。
【数据范围】
30%的数据满足 N<=16
60%的数据满足 N<=1,000
100%的数据满足 N<=100,000
绕绕绕最后是求最长不下降子序列在原始序列的位置
(实现起来就是从头跑一遍不下从尾跑一遍下降)
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 inline int read(){ 7 int f=0; 8 char ch=getchar(); 9 while(ch<'0'||ch>'9')ch=getchar(); 10 while(ch>='0'&&ch<='9'){f=f*10+ch-'0';ch=getchar();} 11 return f; 12 } 13 int n,a[100010],b[100010],c[100010],d[100010],e[100010],f[100010],pos,tot; 14 int main() 15 { 16 n=read(); 17 for(int i=1;i<=n;i++) 18 a[i]=read(); 19 for(int i=1;i<=n;i++){ 20 if(a[i]>=c[tot]){ 21 c[++tot]=a[i]; 22 b[i]=tot; 23 } 24 else { 25 pos=upper_bound(c+1,c+tot,a[i])-c; 26 c[pos]=a[i]; 27 b[i]=pos; 28 } 29 } 30 for(int i=n;i>=1;i--){ 31 if(b[i]==tot||f[b[i]+1]>=a[i]){ 32 d[i]=1; 33 e[b[i]]++; 34 f[b[i]]=max(f[b[i]],a[i]); 35 } 36 } 37 printf("%d ",tot); 38 for(int i=1;i<=n;i++){ 39 if(d[i]&&e[b[i]]==1) 40 printf("%d ",i); 41 } 42 return 0; 43 }
T4选修课(course)
【题目描述】
温州中学开放了许多选修课,每节选修课都属于一种种类。精力旺盛的黄小龙同学想要尽可能多的参加选修课,但是他只能选择M种种类的课程。当然,对于他所选的种类,他会去上所有该种类的课。现在他想知道他最多能上几节选修课,以及上最多选修课的方案数。
两种方案被认为不同当且仅当一种方案中存在另一种方案所没有的选修课。
【输入】course.in
第一行一个只由小写字母组成,长度为N的字符串。表示有N节选修课,以及每节选修课的种类。
第二行一个正整数M。
【输出】course.out
输出一行,两个用空格隔开的正整数,分别表示最多选修课数量和方案数。
【样例输入1】
abcde
1
【样例输出1】
1 5
【样例输入2】
ababac
2
【样例输出2】
5 1
【数据规模】
对于30%的数据,M==1;
对于另外30%的数据,每种种类的选修课最多只有一节;
对于100%的数据1<=M<=26、1<=N<=100000;
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<string> 6 #define ll long long 7 using namespace std; 8 const ll N=100010; 9 int m;string s; 10 int ans_1;//最多选修课数量 11 ll ans_2=1;//方案数 12 ll num[30];//每个课程的数量 13 ll need[N];//need[i]需要i节的课程种类数 14 ll qaq[N];//qaq[i]拥有i节的课程种类数 15 bool cmp(const int &a,const int &b){ 16 return a>b; 17 } 18 ll cal(int n,int m){ 19 if(n==m)return 1; 20 m=max(n-m,m) 21 ll f1=1; 22 for(int i=n;i>m;i--){ 23 f1*=i; 24 } 25 ll f2=1; 26 for(int i=n-m;i>=1;i--){ 27 f2*=i; 28 } 29 return f1/f2; 30 } 31 int main() 32 { 33 //freopen("course.in","r",stdin); 34 //freopen("course.out","w",stdout); 35 cin>>s; 36 scanf("%d",&m);//能选几种 37 int len=s.length(); 38 for(int i=0;i<len;i++){ 39 num[s[i]-'a'+1]+=1; 40 } 41 sort(num+1,num+27,cmp); 42 int max1=num[1]; 43 int min1=num[m]; 44 for(int i=1;i<=26;i++){ 45 qaq[num[i]]++; 46 } 47 for(int i=1;i<=m;i++){ 48 need[num[i]]++; 49 ans_1+=num[i]; 50 } 51 printf("%d ",ans_1); 52 53 for(int i=max1;i>=min1;i--){ 54 if(need[i]){ 55 ll m=cal(qaq[i],need[i]); 56 ans_2*=m; 57 } 58 } 59 cout<<ans_2; 60 return 0; 61 }
ps差点就傻fufu打了高精!其实不用啊
组合数学习https://www.jianshu.com/p/b1c93016410b