A.枚举
B.枚举
C.模拟
D.日期
E.并查集(合并的时候竟然写错 写完检查代码)
//E题代码
#include
using namespace std;
int g[7][7];
//a b c d e f g
//0 1 2 3 4 5 6
int fa[7];
int ff(int x){return fa[x]==x?x:fa[x]=ff(fa[x]);}
int main(){
g[0][1]=g[0][5]=g[1][6]=g[1][2]=g[2][6]=
g[2][3]=g[3][4]=g[4][5]=g[4][6]=g[5][6]=1;
int ans=0;
for(int i=0;i<7;i++){
for(int j=0;j<7;j++){
if(g[i][j])g[j][i]=1;
}
}
for(int i=1;i<1<<7;i++){
bool vis[7]={false};
for(int j=0;j<7;j++)fa[j]=j;
int cur=i,num=0;
while(cur){
if(cur%2)vis[num]=true;
cur/=2;
num++;
}
for(int j=0;j<7;j++){
for(int k=0;k<7;k++){
if(vis[j]&&vis[k]&&g[j][k]){
int fj=ff(j),fk=ff(k);
fa[fj]=fk;
}
}
}
int f=-1,fl=1;
for(int j=0;j<7;j++){
if(!vis[j])continue;
if(f==-1)f=ff(j);
else if(f!=-1&&ff(j)!=f){
fl=0;break;
}
}
if(fl) ans++;
else {
for(int j=0;j<7;j++)cout<<vis[j];cout<<endl;
}
}
cout<<ans<<endl;
}
四舍五入用了这个:
n u m num num四舍五入到小数点后n位
n u m = ( f l o o r ( n u m + 5 × 1 0 − n + 1 ) × 100 ) / 100 num=(floor(num+5×10^-n+1^)×100)/100 num=(floor(num+5×10−n+1)×100)/100
或者
n u m = ( c e i l ( n u m − 5 × 1 0 − n + 1 ) × 100 ) / 100 num=(ceil(num-5×10^-n+1^)×100)/100 num=(ceil(num−5×10−n+1)×100)/100
#include
using namespace std;
const int N=1e5+10;
int a[N];
int maxx=0,minn=100;
int main(){
int n;cin>>n;
double all=0;
for(int i=1;i<=n;i++){
cin>>a[i];
maxx=max(maxx,a[i]),minn=min(minn,a[i]);
all+=a[i];
}
double eval=(floor((all/n+0.005)*100)/100);
printf("%d\n%d\n%.2lf",maxx,minn,eval);
return 0;
}
感觉枚举会超时,所以通过打表将全部合法的回文日期和ABABBABA型日期保存下来(升序),然后二分查找。
#include
using namespace std;
int a[10];
int n1=9899;
int mon[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
bool legal(int year,int month,int day){
if((year%4==0&&year%100!=0)||year%400==0)mon[2]++;
if(month>0&&month<13&&day>0&&day<mon[month]){
if(mon[2]==29)mon[2]--;return true;
}
if(mon[2]==29)mon[2]--;
return false;
}
int main(){
freopen("output.txt","w",stdout);
int cnt=0;
for(int i=1000101;i<100000000;i++){
int cur=i;
if(!legal(cur/10000,cur%10000/100,cur%100))continue;
for(int j=0;j<8;j++){
a[7-j]=cur%10;cur/=10;
}
int fl=1;
for(int j=0;j<4;j++){
if(a[j]!=a[7-j]){
fl=0;break;
}
}
if(fl){
cout<<i<<',';cnt++;
}
}
cout<<cnt<<endl;
return 0;
}
#include
using namespace std;
const int n1=354;
int a[n1]={打表出来的n1个回文日期};
int b[10];
int main(){
freopen("output.txt","w",stdout);
int cnt=0;
for(int i=0;i<n1;i++){
int cur=a[i];
int fl=0;
for(int j=0;j<8;j++){
b[7-j]=cur%10;cur/=10;
}
if(b[0]==b[2]&&b[1]==b[3])fl=1;
if(fl){
cout<<a[i]<<',';
cnt++;
}
}cout<<cnt<<endl;
return 0;
}
#include
using namespace std;
const int n1=354;
const int n2=12;
int a[n1]={打表出来的n1个回文日期};
int b[n2]={打表出来的n2个ABABBABA型的日期};
int main(){
int date;cin>>date;
cout<<a[upper_bound(a,a+n1,date)-a]<<endl;//a数组中第一个大于num的数字
cout<<b[upper_bound(b,b+n2,date)-b];
return 0;
}
很直观的一点是:增加一个交点,就会增加一个区域,即该新产生的交点将原来的一个区域拆分成两个区域。同时每次多一条边,不管有没有产生新的交点,都会增加一个区域。因此每增加一条边,计算由它多产生的交点个数 k i k_i ki,则该边新增的区域个数为 k i + 1 k_i+1 ki+1。
#include
using namespace std;
struct Node{int k,b;}node[1000];
const double inf=1e18;
struct Point{
double x,y;
bool cmp(const Point a){
return a.x==x&&a.y==y;
}
};
Point cal(Node a,Node b){
if(a.k==b.k)return {inf,inf};
double k1=a.k,b1=a.b,k2=b.k,b2=b.b;
return Point{(b2-b1)/(k1-k2),k2*(b2-b1)/(k1-k2)+b2};
}
int main(){
int n;cin>>n;
int cnt=0;
for(int i=1;i<=n;i++){
int x,y;cin>>x>>y;
int fl=0;
for(int j=1;j<=cnt;j++){
if(x==node[j].k&&y==node[j].b){
fl=1;break;
}
}
if(fl)continue;
node[++cnt]={x,y};
}
int ans=2;//只要有一条边就有两个区域
for(int i=2;i<=cnt;i++){
vector<Point>v;
for(int j=1;j<i;j++){
int fl=0;
Point point=cal(node[j],node[i]);
if(point.x==inf)continue;
for(int k=0;k<v.size();k++){
if(point.cmp(v[k])){fl=1;break;}
}
if(!fl)v.push_back(point);
}
ans+=v.size()+1;
}
cout<<ans<<endl;
return 0;
}
/* 子串无法枚举,有很多情况都是重复的,因为一个子串后再加上一个字符形成新的子串,如果该字符已经在原来的子串中出现过了,那么新子串的分值和原来一样,否则应该在原子串的分值基础上加1。应该就是由这个基本的子串间转换删除冗余时间和空间来获得答案。
考虑dp,由小的子串形成新的字符。用到dp首先用目标状态来确定状态设置和状态转移方程。最终的答案是由整个字符串的子串的分值和,我们设 d p [ i ] dp[i] dp[i]为以 i i i为结尾 位置的子串分值和。最后 d p [ n − 1 ] dp[n-1] dp[n−1]即为答案。用上面的子串间转换来确定状态转移方程:前面 i − 1 i-1 i−1个的子串分值和已经求得,现在多了一个字符,以递增的新子串个数枚举包含新加的字符而产生的新子串:
包含一个字符的新子串:1
包含两个字符的新子串 :若 s t r [ i − 1 ] = = s t r [ i ] str[i-1]==str[i] str[i−1]==str[i],则答案为1,否则答案为2
…
考虑到数据范围是 1 e 5 1e5 1e5,因此查找长度的复杂度应该缩小,我思考了一个复杂度为O(nlog2n)的做法:二分+st表。因为是静态的,并且在考虑新子串时需要计算 第 i 第i 第i个字符是否有出现过,考虑使用st表。二分查找最后一个位置 k k k,使得从第 0 0 0个位置到第 k k k个位置字符 s t r [ i ] str[i] str[i]的出现次数为0,则从 k + 1 k+1 k+1到 i − 1 i-1 i−1, s t r [ i ] str[i] str[i]没有出现,因此可由
其实还是隔开了很多的字符,最后一个出现 s t r [ i ] str[i] str[i]的位置,后面的将答案*2,前面的答案不变。 */
//然而我不会写 暴论:这是个思维题 !
枚举子串必定会爆,那就枚举每个位置的字符对结果的贡献(真是个小机灵鬼,这应该是很常见的技巧),即每个位置的字符可以导致哪些包含它的子串增加分值。
设当前考虑的位置为 i i i, i i i 设前面的字符个数是 l l l,则 l = i l=i l=i, i i i位置的字符为 s t r [ i ] str[i] str[i],下一个字符为 s t r [ i ] str[i] str[i]的位置为 n x t [ s t r [ i ] ] nxt[str[i]] nxt[str[i]],设i到下一字符之间的字符个数(不包含i和下一位置)为 r r r,则 i i i位置的字符可以对以下子串产生贡献:以 s t r [ i ] str[i] str[i]为结尾的一共有 l l l个,以 s t r [ i ] str[i] str[i]开头的子串有 r r r个,仅以 s t r [ i ] str[i] str[i]一个字符为子串有 1 1 1个,而 s t r [ i ] str[i] str[i]在中间的有 l ∗ r l*r l∗r个,一共有 l + r + 1 + l ∗ r l+r+1+l*r l+r+1+l∗r个,枚举每个位置即可。
#include
#define ll long long
#define ms(n) memset(n,-1,sizeof n)
using namespace std;
const int N=1e5+10;
int nxt[N],last[N];
int main(){
ms(nxt),ms(last);
ll ans=0;
string str;cin>>str;
for(int i=0;i<str.length();i++){
if(last[str[i]]!=-1)
nxt[last[str[i]]]=i;
last[str[i]]=i;
}
for(int i=0;i<str.length();i++){
ll l=i,r=str.length()-i-1;
if(nxt[i]!=-1)r=nxt[i]-i-1;
ans+=l+r+1+l*r;
}
cout<<ans<<endl;
return 0;
}
使新获得的字串最短,即对该字串采用冒泡排序的交换次数应最多,即其字典序是最大的,题目要求使用字典序最小的,因此要用靠前的字母。如果将相同的字母放在连续的位置,则每个字符的交换次数为最后一个字符之后的字母个数。