取 一定能构成
int a,b,c,d,T;
int main() {
qr(T); while(T--) {
qr(a); qr(b); qr(c); qr(d);
pr1(b); pr1(c); pr2(c);
}
return 0;
}
暴力用操作1,再用操作2.
注意操作1不要增即可.
int T,x,n,m;
int main() {
qr(T); while(T--) {
qr(x); qr(n); qr(m);
while(n&&x>(x/2)+10) n--,x=x/2+10;
if(m*10>=x) puts("YES");
else puts("NO");
}
return 0;
}
最优情况下,一个点被设为工业城市当且仅当子树内所有点都为工业城市.(微扰证明(交换))
所以用堆存 即可.
考试的时候想到了,但是不知道为啥打挂了(WA#6)
priority_queue<int>q;
int n,m,fa[N],dep[N],sz[N];
ll ans;
struct edge{int y,next;}a[N<<1]; int len,last[N];
void ins(int x,int y) {a[++len]=(edge){y,last[x]};last[x]=len;}
void dfs(int x) {
for(int k=last[x],y;k;k=a[k].next)
if((y=a[k].y)^fa[x]) {
fa[y]=x;
dep[y]=dep[x]+1;
dfs(y);
sz[x]+=sz[y];
}
q.push(dep[x]-sz[x]);
sz[x]++;
}
int main() {
qr(n); qr(m);
for(int i=1,x,y;i<n;i++)
qr(x),qr(y),ins(x,y),ins(y,x);
dfs(1);
while(m--) ans+=q.top(),q.pop();
pr2(ans);
return 0;
}
有点思维难度.
有个简单性质.
对于确定的 ,那么 离 越远,总代价越大.
也就是我们取中间的决策优于取两边的决策.(决策包容性)
所以我们可以枚举所有两端为 的情况,然后用二分找到中间位置即可.
int T,a[N],b[N],c[N],n,m,q;
ull ans;
void upd(ll x,ll y,ll z) {
ans=min(ans,(ull)(x-y)*(x-y)+(x-z)*(x-z)+(y-z)*(y-z));
}
int main() {
a[0]=b[0]=c[0]=-1e9;
qr(T); while(T--) {
qr(n); qr(m); qr(q);
for(int i=1;i<=n;i++) qr(a[i]);
sort(a+1,a+n+1);
for(int j=1;j<=m;j++) qr(b[j]);
sort(b+1,b+m+1);
for(int i=1;i<=q;i++) qr(c[i]);
sort(c+1,c+q+1);
a[n+1]=b[m+1]=c[q+1]=2e9;//注意边界
ans=-1;
int x=1,y=1;
while(x<=n) {//x,y
while(y<m&&b[y]<=a[x]) y++;
for(int t=-1,*p;t<=0;t++)
upd(a[x],b[y+t],*(p=lower_bound(c+1,c+q+2,(a[x]+b[y+t])/2))),
upd(a[x],b[y+t],*(--p));
x++;
}
x=y=1;
while(x<=n) {//x,z
while(y<q&&c[y]<=a[x]) y++;
for(int t=-1,*p;t<=0;t++)
upd(a[x],c[y+t],*(p=lower_bound(b+1,b+m+2,(a[x]+c[y+t])/2))),
upd(a[x],c[y+t],*(--p));
x++;
}
x=y=1;
while(x<=m) {//y,z
while(y<q&&c[y]<=b[x]) y++;
for(int t=-1,*p;t<=0;t++)
upd(b[x],c[y+t],*(p=lower_bound(a+1,a+n+2,(b[x]+c[y+t])/2))),
upd(b[x],c[y+t],*(--p));
x++;
}
pr2(ans);
}
return 0;
}
考试的时候一眼看出要用 做,可是呢…
题目要求我们用 的某个前缀通过操作得到一个前缀为 的字符串.( )
假设我们已经表达出了 ,那么如果 ,就可表达出
所以很明显这是个区间 .特别地, 可以匹配任意字符.因为我们只在乎 完成匹配即可.
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=3010,mod= 998244353;
void upd(int &x) {x-=mod; x+=x>>31&mod;}//x的合法范围[0,2*mod)
int n,m,f[N][N],i,j,ans;
//f[i][j]表示t[i...j]由s[1...(j-i+1)]弄出的方案数.特别地,对于t[m]之后的字符可以匹配一切s中的字符.(我们只在乎前缀匹配)
char s[N],t[N];
int main() {
scanf("%s %s",s+1,t+1);
n=strlen(s+1);
m=strlen(t+1);
for(i=1;i<=m;i++) f[i][i]=(t[i]==s[1])*2;
for( ;i<=n;i++) f[i][i]=2;
for(i=2;i<=n;i++) {//枚举长度
for(j=1;j+i-1<=n;j++)
upd(f[j][j+i-1]=f[j][j+i-2]*(j+i-1>m||t[j+i-1]==s[i])+f[j+1][j+i-1]*(j>m||t[j]==s[i]));
}
for(i=m;i<=n;i++) upd(ans+=f[1][i]);
printf("%d\n",ans); return 0;
}
如果 次以上才判WA的话,那么我应该可以在比赛时AC.
次操作的错误代码:(想看就看吧)
int n,a[N],f[N];
ll l1,l2,n1,n2;
int main() {
qr(n); scanf("%lld %lld",&l1,&l2);
for(int i=1;i<=n;i++) {
for(int j=1;j<=2;j++) {
printf("+ %d\n",i);
fflush(stdout);
scanf("%lld %lld",&n1,&n2);
}
a[i]=sqrt(n1-l1);
l1=n1; l2=n2;
}
printf("! ");
for(int i=1;i<n;i++) printf("%d ",a[i]);
printf("%d",a[n]); fflush(stdout);
return 0;
}
我们之所以需要 次操作,是因为我们没有利用的 的条件.
观察 的变化:
如果有连续的5个数的出现次数
增加一个 对应的数,则
所以如果我们已知 那么就可以用一次操作求 了.
知道这个之后,我们的突破口就是就是把 逼到角落( ).使得参数变少,解出确定的值.
只需两次操作,即可求得 的具体值.然后我们尝试推出前面的数.
因为这次牺牲了两次操作,我们自然想到把剩下的操作分配给
.(什么鬼,哪自然了)
同时,假如最后取两次 的话, 的值是相等的,我们是缺乏条件解出 的.
所以我们考虑设计最后三次为 .
剩下的自己推一下吧~~
这题貌似:
思维难度:省选-
代码难度:普及—
#include<cstdio>
using namespace std;
const int N=110;
int n,l1,l2,t[N],s[N],a,b,ans[N];
void q(int i,int &t,int &s) {
printf("+ %d\n",i);
fflush(stdout);
scanf("%d %d",&t,&s);
t-=l1; s-=l2;
l1+=t; l2+=s;
}
int Sqrt(int x) {
int i=1;
while(i*i<=x) i++;
return i-1;
}
int main() {
scanf("%d %d %d",&n,&l1,&l2);
for(int i=2;i<=n-2;i++) q(i,t[i],s[i]);
q(n,t[n],s[n]); q(n-1,t[n-1],s[n-1]); q(n,a,b);
ans[n]=Sqrt(a+t[n]);
ans[n-2]=b-s[n]-1;
ans[n-1]=s[n]/(ans[n-2]+1);
ans[n-3]=s[n-1]/(ans[n-2]+1)-ans[n]-2;
for(int i=n-4; i;i--)
ans[i]=(s[i+2]-ans[i+3]*ans[i+4]-(ans[i+1]+1)*ans[i+3])/(ans[i+1]+1)-1;
printf("! "); ans[1]++; for(int i=1;i<=n;i++) printf("%d ",ans[i]); return 0;
}
EA牛逼.
总结
这次比赛暴露出对贪心的生疏,和区间DP的不足,需要加强练习.