题意:给一个01串,把该串划分,使得每个子串都是所在串形成的环中字典序最小的。
题解:把01串中以1、0分界处都拆开,对拆分出来的子串我们再两两比对,可以合并则合并。队友AC代码
#include
#define LL long long
#define ms0(x) memset(x,0,sizeof(x))
#define ms-1(x) memset(x,-1,sizeof(x))
bool vis[300];
using namespace std;
int pos[300];
string save[300];
struct node{
string str;
int num_0;
int num_1;
node(string a):str(a)
{
num_0=0;int j=0;
for(;j<str.size();j++)
{
if(a[j]=='1')
break;
}
num_0=j;
num_1=str.size()-num_0;
}
node(){
}
bool operator >(const node & tmp)const{
string tmp1,tmp2;
tmp1=str+tmp.str;
tmp2=tmp.str+str;
return tmp1<=tmp2;
// if(tmp.num_1==0)
// {
// if(num_1==0)
// return true;
// else
// return false;
// }
// if(num_0==tmp.num_0)
// return num_1<=tmp.num_1;
// else
// return num_0>tmp.num_0;
}
}ans[300];
int main()
{
ios::sync_with_stdio(false);
int t;
cin>>t;string str;
while(t--)
{
cin>>str;
pos[0]=-1;
memset(vis,0,sizeof(vis));
int cnt=1;
for(int j=0;j<str.size();j++)
{
if(str[j]=='1'&&j<str.size()-1&&str[j+1]=='0')
{
save[cnt]=str.substr(pos[cnt-1]+1,j-pos[cnt-1]);
pos[cnt]=j;
cnt++;
}
}
save[cnt]=str.substr(pos[cnt-1]+1,str.size()-1-pos[cnt-1]);
pos[cnt]=str.size()-1;
cnt++;
int ans_num=1;
ans[1]=node(save[1]);
for(int j=2;j<cnt;j++)
{
node tmp=node(save[j]);
if(ans[ans_num]>tmp)
{
ans[ans_num].str+=tmp.str;
}
else
{
ans[++ans_num]=tmp;
}
}
while(ans_num>1)
{
int tmpnum=1; bool flag=0;
for(int j=2;j<=ans_num;j++)
{
if(ans[tmpnum]>ans[j])
{
ans[tmpnum].str+=ans[j].str;
flag=1;
}
else
{
ans[++tmpnum]=ans[j];
}
}
ans_num=tmpnum;
if(flag==0)
break;
}
for(int j=1;j<=ans_num;j++)
cout<<ans[j].str<<" ";
cout<<endl;
// for(int j=1;j
// {
// cout<
// }
// cout<
}
return 0;
}
(窝太菜了做题太少,没发现是原题POJ2126)
题意:给一个多项式的系数 a n 、 a n − 1 、 . . . a 1 、 a 0 a_n、a_{n-1}、...a_1、a_0 an、an−1、...a1、a0,问该多项式是否可以继续分解。
题解:有个结论,系数大于3时一定是可以继续拆解的,系数小于<2时显然不能继续拆解;系数等于2时我们再用一元二次方程求根公式判断下。(对不起高代老师QAQ,明明上课学过的)
#include
#define LL long long
#define ms0(x) memset(x,0,sizeof(x))
#define ms-1(x) memset(x,-1,sizeof(x))
int num[23];
using namespace std;
int main()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
for(int j=1;j<=n+1;j++)
cin>>num[j];
if(n>2)
{
cout<<"No"<<endl;
}
else
{
if(n<2)
cout<<"Yes"<<endl;
else
{
if(num[2]*num[2]-4*num[1]*num[3]>=0)
{
cout<<"No"<<endl;
}
else
cout<<"Yes"<<endl;
}
}
}
return 0;
}
题意:给定n棵树,每棵树有高度hi、被砍需要的代价ci、数量pi,为了防风沙,需要把这些树形成一个防风墙,条件是最高的树超过(划重点)当前所有树的一半
题解:利用了ci只有200的限制,用ci存储对应的数量和花费,把树按高度从大到小,每次砍树时贪心的坎代价小的树,每次移动更新下ci数组的值即可,复杂度O(200*n)
PS:比赛时坑队友了,各种地方没开ll,more than这么显眼的字没看
#include
using namespace std;
#define ll long long
const int maxn=100010;
const int maxm=220;
#define inf 0x3f3f3f3f3f3f3f3f
int n;
ll Count[maxm];
ll cost[maxm];
struct node{
ll h,c,p;
}tr[maxn];
bool cmp(const node &x,const node &y)
{
return x.h>y.h;
}
int main()
{
while(~scanf("%d",&n)){
int m=200;
for(int i=0;i<=m;i++) Count[i]=cost[i]=0;
ll all=0;//number of trees
ll h,c,p;
for(int i=1;i<=n;i++){
scanf("%lld%lld%lld",&h,&c,&p);
tr[i].h=h;tr[i].c=c;tr[i].p=p;
Count[c]+=p;
all+=p;
cost[c]+=1LL*c*p;
}
sort(tr+1,tr+1+n,cmp);
ll ans=inf;
ll tmp,pre=0,pre2=0;//pre2 高于当前树的树数量 pre高于当前树的树cut代价
for(int i=1,k;i<=n;i=k+1){
tmp=pre;
ll num=0;
for(k=i;k<=n&&tr[k].h==tr[i].h;k++) num+=tr[k].p;//
k--;//
pre2+=num;//pre2 + num
num=max(all-pre2-num+1,0LL);//多坎一棵
for(int j=i;j<=k;j++){
Count[tr[j].c]-=1LL*tr[j].p;
cost[tr[j].c]-=1LL*tr[j].p*tr[j].c;
pre+=1LL*tr[j].p*tr[j].c;
}
for(int j=0;j<=m&#j++){
if(Count[j]){
if(num>=Count[j]) num-=Count[j],tmp+=cost[j];
else tmp+=1LL*j*num,num=0;
}
}
ans=min(ans,tmp);
}
printf("%lld\n",ans);
}
return 0;
}
题意:给n和p,求能被p整除的数位长为n的数,只需地位补零即可。
#include
using namespace std;
#define ll long long
const int maxn=100010;
int getl(int x)
{
int num=0;
while(x){
num++;
x/=10;
}
return num;
}
int main()
{
int n,p;
while(~scanf("%d%d",&n,&p)){
int len=getl(p);
if(len<=n){
printf("%d",p);
for(int i=len+1;i<=n;i++) printf("0");
}else{
printf("T_T");
}
printf("\n");
}
return 0;
}
题意:给n个操作,每次加入[l,r]这些数,加完后问当前的中位数
题解:线段树,每次更新线段,求当前线段树的中间值,难点是对于区间的离散化。
(区间修改也可以离散化的说
#include
using namespace std;
const int maxn=400010*2;
#define inf 0x3f3f3f3f
#define ll long long
int n;
int L[maxn],R[maxn];
int h[maxn*2],tot;
ll sum[maxn<<2];
ll lazy[maxn<<2];
void pushup(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void pushdown(int rt,int l,int r)
{
lazy[rt<<1]+=lazy[rt];
lazy[rt<<1|1]+=lazy[rt];
int m=(l+r)>>1;
//注意这里 需要乘以(h[])
sum[rt<<1]+=lazy[rt]*(h[m]-h[l-1]);
sum[rt<<1|1]+=lazy[rt]*(h[r]-h[m]);
lazy[rt]=0;
}
void build(int rt,int l,int r)
{
if(l==r){
sum[rt]=0;return;
}
int m=(l+r)>>1;
build(rt<<1,l,m);
build(rt<<1|1,m+1,r);
pushup(rt);
}
void update(int rt,int l,int r,int a,int b)
{
if(l>=a&&r<=b){
sum[rt]+=h[r]-h[l-1];//加的是实际个数,需要用到h[]
lazy[rt]++;
return;
}
if(lazy[rt]) pushdown(rt,l,r);
int m=(l+r)>>1;
if(a<=m) update(rt<<1,l,m,a,b);
if(m<b) update(rt<<1|1,m+1,r,a,b);
pushup(rt);
}
ll query(int rt,int l,int r,ll cur)
{
if(l==r){
ll k=sum[rt]/(h[r]-h[l-1]);//找出 当前区间覆盖了几次
return (cur+k-1)/k+h[l-1];//求cur到了哪一位
}
if(lazy[rt]) pushdown(rt,l,r);
int m=(l+r)>>1;
if(cur>sum[rt<<1]) return query(rt<<1|1,m+1,r,cur-sum[rt<<1]);
return query(rt<<1,l,m,cur);
}
int get(int x)
{
return lower_bound(h+1,h+tot+1,x)-h;//返回离散化后的下标
}
int main()
{
int x1,x2,a1,b1,c1,m1;
int y1,y2,a2,b2,c2,m2;
while(~scanf("%d",&n)){
scanf("%d%d%d%d%d%d",&x1,&x2,&a1,&b1,&c1,&m1);
scanf("%d%d%d%d%d%d",&y1,&y2,&a2,&b2,&c2,&m2);
//所有查询区间变成左开右闭
L[1]=min(x1,y1);R[1]=max(x1,y1)+1;
L[2]=min(x2,y2),R[2]=max(x2,y2)+1;
tot=0;
h[++tot]=L[1];h[++tot]=R[1];
h[++tot]=L[2];h[++tot]=R[2];
int x3,y3;
for(int i=3;i<=n;i++){
x3=((1LL*a1*x2%m1+1LL*b1*x1%m1)%m1+c1)%m1;
y3=((1LL*a2*y2%m2+1LL*b2*y1%m2)%m2+c2)%m2;
//原题Li Ri的生成分别是min+1 max+1
L[i]=min(x3,y3);//所有查询区间变成左开右闭 Li减 1
R[i]=max(x3,y3)+1;
x1=x2;y1=y2;
x2=x3;y2=y3;
h[++tot]=L[i];h[++tot]=R[i];
}
// for(int i=1;i<=n;i++){
// printf("L[%d]:%d R[%d]:%d\n",i,L[i]+1,i,R[i]);
// }
sort(h+1,h+tot+1);
tot=unique(h+1,h+tot+1)-h-1;
build(1,1,tot);
ll all=0;
for(int i=1;i<=n;i++){
int l=get(L[i]),r=get(R[i]);
update(1,1,tot,l+1,r);//这里传参注意把左边界+1
all+=(R[i]-L[i]);
//这里的all相当于sum[1]
ll ans=query(1,1,tot,(all+1)/2);
printf("%d\n",ans);
}
}
return 0;
}
/*
2 4 1 8 3 8 0 3 6 9
0 1 2 3 3 4 6 8 8 9
0 1 2 3 4 6 8 9
*/
参考:https://blog.csdn.net/kidsummer/article/details/98985376
题意:有1-n n个能量石头,每个石头有初始值E_i,每个单位时间增加L_i值,上限为C_i值,m次操作,每次在时间ti,选择区间[l,r]的石头并获取它们的能量,获取后石头的能量为0。问最后获取了多少能量。
题解:
考虑每个石头对答案的贡献, 首先要算出来每个石头能量集满要多长时间 d.
然后要统计每个石头询问的时间差,
时间差 >= d, 加上 c. 统计个数 直接 * c,
时间差 < d, 加上 时间差*l[i].统计所有的时间和 * l.
然后还要计算一下初始状态. e.随便搞搞就好了.
然后我们如何维护时间差呢?
用 set + 树状数组维护,
每次把时间点加入到 set 中, 就会有可能减少一个时间段,加上两个小的时间段,
然后我们用树状数组维护时间段.
两个树状数组分别维护
1.每个时间段的个数. 求 >= d 一共有多少个时间段.
2.每个时间段一共有多少时间. 求 < d 一共有多少时间,
要注意除0 的情况.
代码:
#include
using namespace std;
const int maxn=100010;
const int maxT=200010;//--
#define inf 0x3f3f3f3f
#define ll long long
namespace BIT{
ll sum[maxT],sum2[maxT];
int n;
void init(int _n){
n=_n;
for(int i=0;i<=n;i++) sum[i]=0,sum2[i]=0;
//memset(sum,0,sizeof(sum));memset(sum2,sizeof(sum2));
}
int lowbit(int x){
return x&(-x);
}
void Add1(int x,ll val){//
for(;x<=n;sum[x]+=val,x+=lowbit(x));
}
ll getsum1(int x){
ll res=0;
for(;x>0;res+=sum[x],x-=lowbit(x));
return res;
}
void Add2(int x,ll val){//
for(;x<=n;sum2[x]+=val,x+=lowbit(x));
}
ll getsum2(int x){
ll res=0;
for(;x>0;res+=sum2[x],x-=lowbit(x));
return res;
}
};
vector<int> f[maxn],g[maxn];
int e[maxn],l[maxn],c[maxn],d[maxn];
set<int> st;
int n,m;
int main()
{
int tt,cas=1;
scanf("%d",&tt);
while(tt--){
scanf("%d",&n);
int mx=0;
for(int i=1;i<=n;i++) f[i].clear(),g[i].clear();
for(int i=1;i<=n;i++){
scanf("%d%d%d",&e[i],&l[i],&c[i]);
if(l[i]==0||c[i]==0) d[i]=0;
else d[i]=(c[i]-1)/l[i]+1;
mx=max(mx,d[i]);//---
}
int a,b,t;
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&t,&a,&b);
f[a].push_back(t);
g[b].push_back(t);
mx=max(mx,t);
}
ll ans=0;
int num=0;//number of left binary
st.clear();
BIT::init(mx);//
set<int>::iterator it;//
for(int i=1;i<=n;i++){
for(int j=0;j<(int)f[i].size();j++){
num++;
int cur=f[i][j];
if(st.size()==0){
st.insert(cur);continue;
}
//auto it=st.lower_bound(cur);
it=st.lower_bound(cur);
if(it==st.begin()){
int now=(*it)-cur;
BIT::Add1(now,now);
BIT::Add2(now,1);
}else if(it==st.end()){
it--;
int now1=cur-(*it);
BIT::Add1(now1,now1);
BIT::Add2(now1,1);
}else{
int now=(*it);it--;
int now1=(*it);
BIT::Add1(now-cur,now-cur);
BIT::Add1(cur-now1,cur-now1);
BIT::Add1(now-now1,-(now-now1));
BIT::Add2(now-cur,1);
BIT::Add2(cur-now1,1);
BIT::Add2(now-now1,-1);
}
st.insert(cur);
}
ll ans1=1LL*(num-1-BIT::getsum2(d[i]-1))*c[i];
ll ans2=1LL*BIT::getsum1(d[i]-1)*l[i];
ll ans3=min(1LL*c[i],1LL*e[i]+1LL*l[i]*(*st.begin()));
if(num){
if(!d[i]) ans+=e[i];
else ans+=ans1+ans2+ans3;
}
for(int j=0;j<(int)g[i].size();j++){
num--;
int cur=g[i][j];
//auto it=st.find(cur);
it=st.find(cur);
if((int)st.size()==1){
st.erase(cur);continue;// erase(cur) not erase(it)!!
}
if(it==st.begin()){
it++;
int now=(*it)-cur;
BIT::Add1(now,-now);
BIT::Add2(now,-1);
}else{
it++;
if(it==st.end()){
it--;it--;
int now1=cur-(*it);
BIT::Add1(now1,-now1);
BIT::Add2(now1,-1);
}else{
int now=(*it);
it--;it--;
int now1=(*it);
BIT::Add1(now-cur,-(now-cur));
BIT::Add1(cur-now1,-(cur-now1));
BIT::Add1(now-now1,now-now1);
BIT::Add2(now-cur,-1);
BIT::Add2(cur-now1,-1);
BIT::Add2(now-now1,1);
}
}
st.erase(cur);//
}
}
printf("Case #%d: %lld\n",cas++,ans);
}
return 0;
}
题意:给定A B C(1,1e9),求对数(x,y),使得 1 < = x < = A , 1 < = y < = B 1<=x<=A,1<=y<=B 1<=x<=A,1<=y<=B,且满足x&y>C||x^y
#include
using namespace std;
#define ll long long
const int maxn=100010;
ll dp[100][2][2][2][2];
int a[33],b[33],c[33];
//fa 当前的数是否已小于a,fb同理
//h1 是否已满足x&y<=C h2是否已满足x^y>=C
ll dfs(int x,int fa,int fb,int h1,int h2)
{
if(x==-1) return 1;
ll res=dp[x][fa][fb][h1][h2];//
if(res!=-1) return res;
res=0;
for(int i=0;i<=(fa?1:a[x]);i++){
for(int j=0;j<=(fb?1:b[x]);j++){
if((!h1)&&((i&j)>c[x])) continue;//如果当前位x_i&y_i已大于c_i,那么x&y>c 不满足条件
if((!h2)&&((i^j)<c[x])) continue;//同理
res+=dfs(x-1,fa||(i<a[x]),fb||(j<b[x]),h1||(i&j)<c[x],h2||(i^j)>c[x]);//后面这里不能取等于号噢
}
}
return dp[x][fa][fb][h1][h2]=res;
}
void pre(int a[],int x)
{
for(int i=30;i>=0;i--)
if(x&(1<<i)) a[i]=1;
else a[i]=0;
}
void print(int a[])
{
printf("------------\n");
for(int i=30;i>=0;i--){
printf("%d",a[i]);
}
printf("\n");
}
int main()
{
int t;scanf("%d",&t);
while(t--){
int A,B,C;
memset(dp,-1,sizeof(dp));
scanf("%d%d%d",&A,&B,&C);
pre(a,A);
pre(b,B);
pre(c,C);
//print(a);print(b);print(c);
ll ans=dfs(30,0,0,0,0);
ans-=max(0,B-C+1);//去掉A取0的情况
ans-=max(0,A-C+1);// 去掉B取0的情况
printf("%lld\n",1LL*A*B-ans);
}
return 0;
}
对于是否为0,可以再加两个位置判断下
#include
using namespace std;
#define ll long long
const int maxn=100010;
ll dp[100][2][2][2][2][2][2];
int a[33],b[33],c[33];
//fa 当前的数是否已小于a,fb同理
//h1 是否已满足x&y<=C h2是否已满足x^y>=C
ll dfs(int x,int fa,int fb,int h1,int h2,int cura,int curb)
{
if(x==-1){
if(cura&&curb) return 1;
return 0;
}
ll &res=dp[x][fa][fb][h1][h2][cura][curb];//
if(res!=-1) return res;
res=0;
for(int i=0;i<=(fa?1:a[x]);i++){
for(int j=0;j<=(fb?1:b[x]);j++){
if((!h1)&&((i&j)>c[x])) continue;//如果当前位x_i&y_i已大于c_i,那么x&y>c 不满足条件
if((!h2)&&((i^j)<c[x])) continue;//同理
res+=dfs(x-1,fa||(i<a[x]),fb||(j<b[x]),h1||(i&j)<c[x],h2||(i^j)>c[x],cura||i,curb||j);
}
}
return res;
}
void pre(int a[],int x)
{
for(int i=30;i>=0;i--)
if(x&(1<<i)) a[i]=1;
else a[i]=0;
}
void print(int a[])
{
printf("------------\n");
for(int i=30;i>=0;i--){
printf("%d",a[i]);
}
printf("\n");
}
int main()
{
int t;scanf("%d",&t);
while(t--){
int A,B,C;
memset(dp,-1,sizeof(dp));
scanf("%d%d%d",&A,&B,&C);
pre(a,A);
pre(b,B);
pre(c,C);
//print(a);print(b);print(c);
ll ans=dfs(30,0,0,0,0,0,0);
printf("%lld\n",1LL*A*B-ans);
}
return 0;
}
题意:给定n m,构造kk的矩阵,该矩阵的每个元素值不小于m,使得选择任意不同行、不同列的k个元素的总和都相等,且不大于n
官方题解:
容易证明,一个 kk 棋盘的排布方案满足“不同行不同列的方格内的玻璃球数量的总和均相
同”要求,当且仅当该方案具有如下形式:
∑ i = 1 k a i A i + ∑ i = 1 k b i B i \sum_{i=1}^{k} a_iAi +\sum_{i=1}^{k} b_iB_i ∑i=1kaiAi+∑i=1kbiBi/,其中 A i ( B i ) A_i(B_i) Ai(Bi)为第i行(列)均为1,其余行(列)均为0的k*k矩阵,又因为该和为T,所以$ a i 和 a_i和 ai和b_i$需满足以下条件:
∑ i = 1 k a i + ∑ i = 1 k = T ( a i ≥ 0 , b i ≥ 0 ) \sum_{i=1}^{k} a_i +\sum_{i=1}^{k} = T (a_i\geq0,b_i\geq0) ∑i=1kai+∑i=1k=T(ai≥0,bi≥0),用隔板法思想,得到满足该条件的方案为 C T + 2 k − 1 2 k − 1 C_{T+2k-1}^{2k-1} CT+2k−12k−1
但是这些方案中其实有一部分是重复的。重复的原因在于:当 a i a_i ai均为非负时,事实上此时将所有 a i a_i ai全部减一,将所有 b i b_i bi全部加一,,将得到一种完全相同的、但我们也记了一次数的方案。我们应该想办法将这种方案全部去除并只留下这种方案的唯一代表。很显然,我们只需要再减去满足:
∑ i = 1 k a i + ∑ i = 1 k = T ( a i ≥ 1 , b i ≥ 0 ) \sum_{i=1}^{k} a_i +\sum_{i=1}^{k} = T (a_i\geq1,b_i\geq0) ∑i=1kai+∑i=1k=T(ai≥1,bi≥0)
的方案数即可。因此所有的方案数即为
C T + 2 k − 1 2 k − 1 − C T + k − 1 2 k − 1 C_{T+2k-1}^{2k-1} -C_{T+k-1}^{2k-1} CT+2k−12k−1−CT+k−12k−1
#include
using namespace std;
const int maxn=6010;
#define inf 0x3f3f3f3f
#define ll long long
const int mod=998244353;
int f[maxn],invf[maxn];
ll quickp(ll a,ll p)
{
ll res=1;
while(p){
if(p&1) res=res*a%mod;
p>>=1;
a=a*a%mod;
}
return res;
}
void init()
{
invf[0]=f[0]=1;
for(int i=1;i<maxn;i++) f[i]=1LL*i*f[i-1]%mod;
invf[maxn-1]=quickp(f[maxn-1],mod-2);
for(int i=maxn-2;i>=1;i--) invf[i]=1LL*invf[i+1]*(i+1)%mod;
}
//开这个爆内存了。。
//int C[maxn][maxn];
//void init()
//{
// C[0][0]=1;
// for(int i=1;i
// C[i][0]=1;
// for(int j=1;j<=i;j++){
// C[i][i-j]=C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
// }
// }
//}
ll C(ll x,ll y)
{
if(y>x) return 0;
return 1LL*f[x]*invf[y]%mod*invf[x-y]%mod;
}
void Add(ll &x,ll y)
{
x+=y;
if(x>=mod) x-=mod;
}
void Sub(ll &x,ll y)
{
x-=y;
if(x<0) x+=mod;
}
int n,m;
int main()
{
int tt;scanf("%d",&tt);
init();
while(tt--){
scanf("%d%d",&n,&m);
ll ans=0;
for(int k=1;k*m<=n;k++){
for(int t=0;t<=n-k*m;t++){
Add(ans,C(t+2*k-1,2*k-1));
Sub(ans,C(t+k-1,2*k-1));
}
}
printf("%lld\n",ans);
}
return 0;
}
求A和Breverse后相加的结果,队友AC代码
#include
#define LL long long
#define ms0(x) memset(x,0,sizeof(x))
#define ms-1(x) memset(x,-1,sizeof(x))
LL a,b;using namespace std;
void change(LL &a)
{
string tmp;
stringstream ss;
ss<<a;
ss>>tmp;
reverse(tmp.begin(),tmp.end());
ss.clear();
ss<<tmp;
ss>>a;
return;
}
int main()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--)
{
cin>>a>>b;
change(a);
change(b);
LL c;
c=a+b;
change(c);
cout<<c<<endl;
}
return 0;
}